<!DOCTYPE html>
<html lang=zh>
<head>
    <meta charset="utf-8">
    
    <title>LErry</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <meta name="description" content="记录生活点滴">
<meta property="og:type" content="website">
<meta property="og:title" content="LErry">
<meta property="og:url" content="https://www.itchina.top/page/5/index.html">
<meta property="og:site_name" content="LErry">
<meta property="og:description" content="记录生活点滴">
<meta property="og:locale" content="zh-CN">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="LErry">
<meta name="twitter:description" content="记录生活点滴">
    

    
        <link rel="alternate" href="/atom.xml" title="LErry" type="application/atom+xml" />
    

    
        <link rel="icon" href="/css/images/shortcut_icon.png" />
    

    <link rel="stylesheet" href="/libs/font-awesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/libs/open-sans/styles.css">
    <link rel="stylesheet" href="/libs/source-code-pro/styles.css">

    <link rel="stylesheet" href="/css/style.css">

    <script src="/libs/jquery/2.1.3/jquery.min.js"></script>
    
    
        <link rel="stylesheet" href="/libs/lightgallery/css/lightgallery.min.css">
    
    
        <link rel="stylesheet" href="/libs/justified-gallery/justifiedGallery.min.css">
    
    
    
    


</head>

<body>
    <div id="container">
        <header id="header">
    <div id="header-main" class="header-inner">
        <div class="outer">
            <a href="/" id="logo">
                <i class="logo"></i>
                <span class="site-title">LErry</span>
            </a>
            <nav id="main-nav">
                
                    <a class="main-nav-link" href="/.">主页</a>
                
                    <a class="main-nav-link" href="/freebooks">书籍</a>
                
                    <a class="main-nav-link" href="/tags">标签</a>
                
                    <a class="main-nav-link" href="/archives">归档</a>
                
                    <a class="main-nav-link" href="/aboutme">关于</a>
                
            </nav>
            
                
                <nav id="sub-nav">
                    <div class="profile" id="profile-nav">
                        <a id="profile-anchor" href="javascript:;">
                            <img class="avatar" src="/css/images/avatar.png" />
                            <i class="fa fa-caret-down"></i>
                        </a>
                    </div>
                </nav>
            
            <div id="search-form-wrap">

    <form class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
        <button type="submit" class="search-form-submit"></button>
    </form>
    <div class="ins-search">
    <div class="ins-search-mask"></div>
    <div class="ins-search-container">
        <div class="ins-input-wrapper">
            <input type="text" class="ins-search-input" placeholder="想要查找什么..." />
            <span class="ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: '文章',
            PAGES: '页面',
            CATEGORIES: '分类',
            TAGS: '标签',
            UNTITLED: '(未命名)',
        },
        ROOT_URL: '/',
        CONTENT_URL: '/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>
<script src="/js/insight.js"></script>

</div>
        </div>
    </div>
    <div id="main-nav-mobile" class="header-sub header-inner">
        <table class="menu outer">
            <tr>
                
                    <td><a class="main-nav-link" href="/.">主页</a></td>
                
                    <td><a class="main-nav-link" href="/freebooks">书籍</a></td>
                
                    <td><a class="main-nav-link" href="/tags">标签</a></td>
                
                    <td><a class="main-nav-link" href="/archives">归档</a></td>
                
                    <td><a class="main-nav-link" href="/aboutme">关于</a></td>
                
                <td>
                    
    <div class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
    </div>

                </td>
            </tr>
        </table>
    </div>
</header>

        <div class="outer">
            
                

<aside id="profile">
    <div class="inner profile-inner">
        <div class="base-info profile-block">
            <img id="avatar" src="/css/images/avatar.png" />
            <h2 id="name">LErry Li</h2>
            <h3 id="title">知我者谓我心忧，不知我者谓我何求</h3>
            <span id="location"><i class="fa fa-map-marker"></i>Shanghai, China</span>
            <a id="follow" target="_blank" href="https://github.com/lerry903">关注我</a>
        </div>
        <div class="article-info profile-block">
            <div class="article-info-block">
                65
                <span>文章</span>
            </div>
            <div class="article-info-block">
                54
                <span>标签</span>
            </div>
        </div>
        
        <div class="profile-block social-links">
            <table>
                <tr>
                    
                    
                    <td>
                        <a href="https://github.com/lerry903" target="_blank" title="github" class=tooltip>
                            <i class="fa fa-github"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="mailto:lerryli@foxmail.com" target="_blank" title="envelope" class=tooltip>
                            <i class="fa fa-envelope"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="http://wpa.qq.com/msgrd?v=3&uin=824444270&site=qq&menu=yes" target="_blank" title="qq" class=tooltip>
                            <i class="fa fa-qq"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="https://weibo.com/5941010376" target="_blank" title="weibo" class=tooltip>
                            <i class="fa fa-weibo"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="/atom.xml" target="_blank" title="rss" class=tooltip>
                            <i class="fa fa-rss"></i>
                        </a>
                    </td>
                    
                </tr>
            </table>
        </div>
        
    </div>
</aside>

            
            <section id="main">
    <article id="post-IntelliJ Idea 常用快捷键" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/IntelliJ Idea 常用快捷键/">IntelliJ Idea 常用快捷键</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/IntelliJ Idea 常用快捷键/">
            <time datetime="2018-04-19T16:34:21.421Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/IDE/">IDE</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/idea/">idea</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>最常用快捷键<br>1.Ctrl＋E，可以显示最近编辑的文件列表<br>2.Shift＋Click可以关闭文件<br>3.Ctrl＋[或]可以跳到大括号的开头结尾<br>4.Ctrl＋Shift＋Backspace可以跳转到上次编辑的地方<br>5.Ctrl＋F12，可以显示当前文件的结构<br>6.Ctrl＋F7可以查询当前元素在当前文件中的引用，然后按F3可以选择<br>7.Ctrl＋N，可以快速打开类<br>8.Ctrl＋Shift＋N，可以快速打开文件<br>9.Alt＋Q可以看到当前方法的声明<br>10.Ctrl＋W可以选择单词继而语句继而行继而函数<br>11.Alt＋F1可以将正在编辑的元素在各个面板中定位<br>12.Ctrl＋P，可以显示参数信息<br>13.Ctrl＋Shift＋Insert可以选择剪贴板内容并插入<br>14.Alt＋Insert可以生成构造器/Getter/Setter等<br>15.Ctrl＋Alt＋V 可以引入变量。例如把括号内的SQL赋成一个变量<br>16.Ctrl＋Alt＋T可以把代码包在一块内，例如try/catch<br>17.Alt＋Up and Alt＋Down可在方法间快速移动<br>下面的不是很有用<br>18.在一些地方按Alt＋Enter可以得到一些Intention Action，例如将”==”改为”equals()”<br>19.Ctrl＋Shift＋Alt＋N可以快速打开符号<br>20.Ctrl＋Shift＋Space在很多时候都能够给出Smart提示<br>21.Alt＋F3可以快速寻找<br>22.Ctrl＋/和Ctrl＋Shift＋/可以注释代码<br>23.Ctrl＋Alt＋B可以跳转到抽象方法的实现<br>24.Ctrl＋O可以选择父类的方法进行重写<br>25.Ctrl＋Q可以看JavaDoc<br>26.Ctrl＋Alt＋Space是类名自动完成<br>27.快速打开类/文件/符号时，可以使用通配符，也可以使用缩写<br>28.Live Templates! Ctrl＋J<br>29.Ctrl＋Shift＋F7可以高亮当前元素在当前文件中的使用<br>30.Ctrl＋Alt＋Up /Ctrl＋Alt＋Down可以快速跳转搜索结果<br>31.Ctrl＋Shift＋J可以整合两行<br>32.Alt＋F8是计算变量值  </p>
<p>IntelliJ IDEA使用技巧一览表<br>在使用 InelliJ IDEA 的过程中，通过查找资料以及一些自己的摸索，发现这个众多Java程序员喜欢的IDE里有许多值得一提的小窍门，如果能熟练的将它<br>们应用于实际开发过程中，相信它会大大节省你的开发时间，而且随之而来的还会有那么一点点成就感：） Try it ！  </p>
<p>1、写代码时用 Alt-Insert （ Code|Generate… ）可以创建类里面任何字段的 getter 与 setter 方法。<br>&lt;?xml:namespace prefix = v ns = “urn:schemas-microsoft-com:vml” /&gt;  </p>
<p>2、右键点击断点标记（在文本的左边栏里）激活速查菜单，你可以快速设置 enable/disable 断点或者条件它的属性。  </p>
<p>3、 CodeCompletion （代码完成）属性里的一个特殊的变量是，激活 Ctrl-Alt-Space<br>可以完成在或不在当前文件里的类名。如果类没有引入则 import 标志会自动创建。  </p>
<p>4、使用 Ctrl-Shift-V 快捷键可以将最近使用的剪贴板内容选择插入到文本。使用时系统会弹出一个含有剪贴内容的对话框，从中你可以选择你要粘贴的部分。  </p>
<p>5、利用CodeCompletion（代码完成）属性可以快速地在代码中完成各种不同地语句，方法是先键入一个类名地前几个字母然后再用 Ctrl-Space<br>完成全称。如果有多个选项，它们会列在速查列表里。  </p>
<p>6 、用 Ctrl-/ 与 Ctrl-Shift-/ 来注释 / 反注释代码行与代码块。  </p>
<p>-/ 用单行注释标记（“ //… ”）来注释 / 反注释当前行或者选择地代码块。而 Ctrl-Shift-/ 则可以用块注释标记（“ /<em>…</em>/ ”）把所选块包围起来。要反注释一个代码块就在块中任何一个地方按 Ctrl-Shift-/ 即可。   </p>
<p>7 、按 Alt-Q （ View|Context Info ）可以不需要移动代码就能查看当前方法地声明。连续按两次会显示当前所编辑的类名。  </p>
<p>8 、使用 Refactor|Copy Class…<br>可以创建一个所选择的类的“副本”。这一点很有用，比如，在你想要创建一个大部分内容都和已存在类相同的类时。  </p>
<p>9 、在编辑器里 Ctrl-D 可以复制选择的块或者没有所选块是的当前行。  </p>
<p>10 、 Ctrl-W （选择字）在编辑器里的功能是先选择脱字符处的单词，然后选择源代码的扩展区域。举例来说，先选择一个方法名，然后是调用这个方法的表达式，<br>然后是整个语句，然后包容块，等等。  </p>
<p>11 、如果你不想让指示事件细节的“亮球”图标在编辑器上显示，通过按 Alt-Enter<br>组合键打开所有事件列表然后用鼠标点击它就可以把这个事件文本附件的亮球置成非活动状态。  </p>
<p>这样以后就不会有指示特殊事件的亮球出现了，但是你仍然可以用 Alt-Enter 快捷键使用它。  </p>
<p>12 、在使用 CodeCompletion 时，可以用逗点（ .<br>）字符，逗号（，）分号（；），空格和其它字符输入弹出列表里的当前高亮部分。选择的名字会随着输入的字符自动输入到编辑器里。  </p>
<p>13 、在任何工具窗口里使用 Escape 键都可以把焦点移到编辑器上。  </p>
<p>Shift-Escape 不仅可以把焦点移到编辑器上而且还可以隐藏当前（或最后活动的）工具窗口。  </p>
<p>F12 键把焦点从编辑器移到最近使用的工具窗口。  </p>
<p>14 、在调试程序时查看任何表达式值的一个容易的方法就是在编辑器中选择文本（可以按几次 Ctrl-W 组合键更有效地执行这个操作）然后按 Alt-F8 。  </p>
<p>15 、要打开编辑器脱字符处使用的类或者方法 Java 文档的浏览器，就按 Shift-F1 （右键菜单的 External JavaDoc ）。  </p>
<p>要使用这个功能须要把加入浏览器的路径，在“ General ”选项中设置（ Options | IDE Settings ），另外还要把创建的 Java<br>文档加入到工程中（ File | Project Properties ）。  </p>
<p>16 、用 Ctrl-F12 （ View | File Structure Popup ）键你可以在当前编辑的文件中快速导航。  </p>
<p>这时它会显示当前类的成员列表。选中一个要导航的元素然后按 Enter 键或 F4 键。要轻松地定位到列表中的一个条目，只需键入它的名字即可。  </p>
<p>17 、在代码中把光标置于标记符或者它的检查点上再按 Alt-F7 （右键菜单中的 Find Usages…<br>）会很快地查找到在整个工程中使用地某一个类、方法或者变量的位置。  </p>
<p>18 、按 Ctrl-N （ Go to | Class… ）再键入类的名字可以快速地在编辑器里打开任何一个类。从显示出来的下拉列表里选择类。<br>同样的方法你可以通过使用 Ctrl-Shift-N （ Go to | File… ）打开工程中的非 Java 文件。  </p>
<p>19 、要导航代码中一些地方使用到的类、方法或者变量的声明，把光标放在查看项上再按 Ctrl-B 即可。也可以通过按 Ctrl<br>键的同时在查看点上单击鼠标键调转到声明处。  </p>
<p>20 、把光标放到查看点上再按 Ctrl-Alt-B 可以导航到一个抽象方法的实现代码。  </p>
<p>21 、要看一个所选择的类的继承层次，按 Ctrl-H （ Browse Type Hierarchy<br>）即可。也可以激活编辑器中的继承关系视图查看当前编辑类的继承关系。22 、使用 Ctrl-Shift-F7 （ Search | Highlight<br>Usages in File ）可以快速高亮显示当前文件中某一变量的使用地方。按 Escape 清除高亮显示。  </p>
<p>23 、用 Alt-F3 （ Search | Incremental Search ）在编辑器中实现快速查查找功能。  </p>
<p>在“ Search for: ”提示工具里输入字符，使用箭头键朝前和朝后搜索。按 Escape 退出。  </p>
<p>24 、按 Ctrl-J 组合键来执行一些你记不起来的 Live Template 缩写。比如，键“ it ”然后按 Ctrl-J 看看有什么发生。  </p>
<p>25 、 Introduce Variable 整合帮助你简化代码中复杂的声明。举个例子，在下面的代码片断里，在代码中选择一个表达式：然后按 Ctrl-<br>Alt-V 。  </p>
<p>26 、 Ctrl-Shift-J 快捷键把两行合成一行并把不必要的空格去掉以匹配你的代码格式。  </p>
<p>27 、 Ctrl-Shift-Backspace （ Go to | Last Edit Location ）让你调转到代码中所做改变的最后一个地方。  </p>
<p>多按几次 Ctrl-Shift-Backspace 查看更深的修改历史。  </p>
<p>28 、用 Tools | Reformat Code… 根据你的代码样式参考（查看 Options | IDE Setting | Code Style<br>）格式化代码。  </p>
<p>使用 Tools | Optimize Imports… 可以根据设置（查看 Options | IDE Setting | Code Style |<br>Imports ）自动“优化” imports （清除无用的 imports 等）。  </p>
<p>29 、使用 IDEA 的 Live Templates | Live Templates 让你在眨眼间创建许多典型代码。比如，在一个方法里键入  </p>
<p>再按 Tab 键看有什么事情发生了。<br>用 Tab 键在不同的模板域内移动。查看 Options | Live Templates 获取更多的细节。  </p>
<p>30 、要查看一个文件中修改的本地历史，激活右键菜单里的 Local VCS | Show History…<br>。也许你可以导航不同的文件版本，看看它们的不同之处再回滚到以前的任何一个版本吧。  </p>
<p>使用同样的右键菜单条目还可以看到一个目录里修改的历史。有了这个特性你就不会丢失任何代码了。  </p>
<p>31 、如果要了解主菜单里每一个条目的用途，把鼠标指针移到菜单条目上再应用程序框架的底部的状态栏里就会显示它们的一些简短描述，也许会对你有帮助。  </p>
<p>32 、要在编辑器里显示方法间的分隔线，打开 Options | IDE Settings | Editor ，选中“ Show method<br>separators ”检查盒（ checkbox ）。  </p>
<p>33 、用 Alt-Up 和 Alt-Down 键可以在编辑器里不同的方法之间快速移动。  </p>
<p>34 、用 F2/Shift-F2 键在高亮显示的语法错误间跳转。  </p>
<p>用 Ctrl-Alt-Down/Ctrl-Alt-Up 快捷键则可以在编译器错误信息或者查找操作结果间跳转。  </p>
<p>35 、通过按 Ctrl-O （ Code | Override Methods… ）可以很容易地重载基本类地方法。  </p>
<p>要完成当前类 implements 的（或者抽象基本类的）接口的方法，就使用 Ctrl-I （ Code | Implement Methods… ）。  </p>
<p>36 、如果光标置于一个方法调用的括号间，按 Ctrl-P 会显示一个可用参数的列表。  </p>
<p>37 、要快速查看编辑器脱字符处使用的类或方法的 Java 文档，按 Ctrl-Q （在弹出菜单的 Show Quick JavaDoc 里）即可。  </p>
<p>38 、像 Ctrl-Q （ Show Quick JavaDoc 显示简洁 Java 文档）， Ctrl-P （ Show Parameter Info<br>显示参数信息）， Ctrl-B （ Go to Declaration 跳转到声明）， Shift-F1 （ External JavaDoc 外部<br>Java 文档）以及其它一些快捷键不仅可以在编辑器里使用，也可以应用在代码完成右键列表里。  </p>
<p>39 、 Ctrl-E （ View | Recent Files ）弹出最近访问的文件右键列表。选中文件按 Enter 键打开。  </p>
<p>40 、在 IDEA 中可以很容易地对你的类，方法以及变量进行重命名并在所有使用到它们的地方自动更正。  </p>
<p>试一下，把编辑器脱字符置于任何一个变量名字上然后按 Shift-F6 （ Refactor | Rename… ）。在对话框里键入要显示地新名字再按<br>Enter 。你会浏览到使用这个变量地所有地方然后按“ Do Refactor ”按钮结束重命名操作。  </p>
<p>41 、要在任何视图（ Project View 工程视图， Structure View 结构视图或者其它视图）里快速  </p>
<p>选择当前编辑地部分（类，文件，方法或者字段），按 Alt-F1 （ View | Select in… ）。  </p>
<p>42 、在“ new ”字符后实例化一个已知类型对象时也许你会用到 SmartType 代码完成这个特性。比如，键入  </p>
<p>再按 Ctrl-Shift-Space ：  </p>
<p>43 、通过使用 SmartType 代码完成，在 IDEA 中创建接口的整个匿名 implementation 也是非常容易的，比如，对于一些<br>listener （监听器），可以键入  </p>
<p>Component component;  </p>
<p>component.addMouseListener(  </p>
<p>new <caret is="" here="">  </caret></p>
<p>);  </p>
<p>然后再按 Ctrl-Shift-Space 看看有什么发生了。  </p>
<p>44 、在你需要设置一个已知类型的表达式的值时用 SmartType 代码完成也很有帮助。比如，键入  </p>
<p>String s = ( <caret is="" here="">  </caret></p>
<p>再按 Ctrl-Shift-Space 看看会有什么出现。  </p>
<p>45 、在所有视图里都提供了速查功能：在树里只需键入字符就可以快速定位到一个条目。  </p>
<p>46 、当你想用代码片断捕捉异常时，在编辑器里选中这个片断，按 Ctrl-Alt-T （ Code | Surround with… ）然后选择“<br>try/catch ”。它会自动产生代码片断中抛出的所有异常的捕捉块。在 Options | File Templates | Code tab<br>中你还可以自己定制产生捕捉块的模板。  </p>
<p>用列表中的其它项可以包围别的一些结构。  </p>
<p>47 、在使用代码完成时，用 Tab 键可以输入弹出列表里的高亮显示部分。  </p>
<p>不像用 Enter 键接受输入，这个选中的名字会覆盖掉脱字符右边名字的其它部分。这一点在用一个方法或者变量名替换另一个时特别有用。  </p>
<p>48 、在声明一个变量时代码完成特性会给你显示一个建议名。比如，开始键入“ private FileOutputStream ”然后按 Ctrl-Space  </p>
<p>在 Options | IDE Setting | Code Style 中还可以为本地变量，参数，实例及静态字段定制名字。  </p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/IntelliJ Idea 常用快捷键/#comments" id="sourceId::2018/04/20/IntelliJ Idea 常用快捷键/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-HTTP 请求" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/HTTP 请求/">HTTP 请求</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/HTTP 请求/">
            <time datetime="2018-04-19T16:34:21.420Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/J2EE/">J2EE</a>
    </div>

                        
                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>Maven配置：</p>
<pre><code>&lt;dependency&gt;  
    &lt;groupId&gt;org.apache.httpcomponents&lt;/groupId&gt;  
    &lt;artifactId&gt;httpclient&lt;/artifactId&gt;  
    &lt;version&gt;4.3&lt;/version&gt;  
&lt;/dependency&gt; 
</code></pre><p>HttpUtils：</p>
<pre><code>package com.lerry.http;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import net.sf.json.JSONObject;

import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpUtils {

    private transient static Logger log = LoggerFactory.getLogger(HttpUtils.class);

    public static String doGet(String url, Map&lt;String,String&gt; param){
        //创建HttpClient对象
        CloseableHttpClient httpclient = HttpClients.createDefault();
        String resultString = &quot;&quot;;
        CloseableHttpResponse httpResponse = null;
        try {
            //创建uri
            URIBuilder builder = new URIBuilder(url);
            if(param!=null &amp;&amp; !param.isEmpty()){
                for(String key :param.keySet()){
                    builder.addParameter(key, param.get(key));
                }
            }
            URI uri = builder.build();
            // 创建httpGet请求
            HttpGet httpGet = new HttpGet(uri);

            // 开始执行http请求
            long startTime = System.currentTimeMillis();
            httpResponse = httpclient.execute(httpGet);
            long endTime = System.currentTimeMillis();

            // 获得响应状态码
            int statusCode = httpResponse.getStatusLine().getStatusCode();  
            log.info(&quot;statusCode:&quot; + statusCode);  
            log.info(&quot;调用API花费时间(单位：毫秒)：&quot; + (endTime - startTime));

            // 取出应答字符串
            HttpEntity httpEntity = httpResponse.getEntity();
            resultString = EntityUtils.toString(httpEntity,Charset.forName(&quot;UTF-8&quot;));
            // 去掉返回结果中的&quot;\r&quot;字符，否则会在结果字符串后面显示一个小方格
            resultString.replaceAll(&quot;\r&quot;, &quot;&quot;);

            // 判断返回状态是否为200
            if (statusCode != HttpStatus.SC_OK) {
                throw new RuntimeException(String.format(&quot;\n\tStatus:%s\n\tError Message:%s&quot;, statusCode,resultString));
            }
        } catch (ClientProtocolException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } catch (URISyntaxException e) {
            log.error(e.getMessage(), e);
        } finally{
            try {
                if(httpResponse != null){
                    httpResponse.close();
                }
                httpclient.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return resultString;
    }

    public static String doGet(String url){
        return doGet(url,null);
    }

    public static String doPost(String url, Map&lt;String,String&gt; param){
        //创建HttpClient对象
        CloseableHttpClient httpclient = HttpClients.createDefault();
        String resultString = &quot;&quot;;
        CloseableHttpResponse httpResponse = null;
        try {
            // 创建HttpPost对象
            HttpPost httpPost = new HttpPost(url);

            if(param!=null &amp;&amp; !param.isEmpty()){
                List&lt;NameValuePair&gt; params = new ArrayList&lt;NameValuePair&gt;();
                for(String key :param.keySet()){
                    params.add(new BasicNameValuePair(key, param.get(key)));
                }
                httpPost.setEntity(new UrlEncodedFormEntity(params, Charset.forName(&quot;UTF-8&quot;)));
            }

            // 开始执行http请求
            long startTime = System.currentTimeMillis();  
            httpResponse = httpclient.execute(httpPost);
            long endTime = System.currentTimeMillis();

            // 获得响应状态码
            int statusCode = httpResponse.getStatusLine().getStatusCode();  
            log.info(&quot;statusCode:&quot; + statusCode);  
            log.info(&quot;调用API花费时间(单位：毫秒)：&quot; + (endTime - startTime));

            // 取出应答字符串
            HttpEntity httpEntity = httpResponse.getEntity();
            resultString = EntityUtils.toString(httpEntity,Charset.forName(&quot;UTF-8&quot;));

            // 判断返回状态是否为200
            if (statusCode != HttpStatus.SC_OK) {
                throw new RuntimeException(String.format(&quot;\n\tStatus:%s\n\tError Message:%s&quot;, statusCode,resultString));
            } 
        } catch (ClientProtocolException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally{
            try {
                if(httpResponse != null){
                    httpResponse.close();
                }
                httpclient.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return resultString;
    }

    public static String doJsonPost(String url, Map&lt;String,String&gt; param){
        String resultString = &quot;&quot;;
        if(param!=null &amp;&amp; !param.isEmpty()){
            JSONObject jsonObject = new JSONObject();
            for(String key :param.keySet()){
                jsonObject.put(key, param.get(key));
            }
            String json = jsonObject.toString();
            resultString = doJsonPost(url,json);
        } else{
            resultString = doJsonPost(url,&quot;&quot;);
        }
        return resultString;
    }

    public static String doJsonPost(String url, String json){
        //创建HttpClient对象
        CloseableHttpClient httpclient = HttpClients.createDefault();
        String resultString = &quot;&quot;;
        CloseableHttpResponse httpResponse = null;
        try {
            // 创建HttpPost对象
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new StringEntity(json,ContentType.APPLICATION_JSON));

            // 开始执行http请求
            long startTime = System.currentTimeMillis();  
            httpResponse = httpclient.execute(httpPost);
            long endTime = System.currentTimeMillis();

            // 获得响应状态码
            int statusCode = httpResponse.getStatusLine().getStatusCode();  
            log.info(&quot;statusCode:&quot; + statusCode);  
            log.info(&quot;调用API 花费时间(单位：毫秒)：&quot; + (endTime - startTime));

            // 取出应答字符串
            HttpEntity httpEntity = httpResponse.getEntity();
            resultString = EntityUtils.toString(httpEntity,Charset.forName(&quot;UTF-8&quot;));

            // 判断返回状态是否为200
            if (statusCode != HttpStatus.SC_OK) {
                throw new RuntimeException(String.format(&quot;\n\tStatus:%s\n\tError Message:%s&quot;, statusCode,resultString));
            } 
        } catch (ClientProtocolException e) {
            log.error(e.getMessage(), e);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally{
            try {
                if(httpResponse != null){
                    httpResponse.close();
                }
                httpclient.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return resultString;
    }

}
</code></pre>
        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/HTTP 请求/#comments" id="sourceId::2018/04/20/HTTP 请求/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-Git命令“从入门到放弃”" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/Git命令“从入门到放弃”/">Git命令“从入门到放弃”</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/Git命令“从入门到放弃”/">
            <time datetime="2018-04-19T16:34:21.419Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/Git/">Git</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/git/">git</a>, <a class="tag-link" href="/tags/github/">github</a>, <a class="tag-link" href="/tags/版本控制系统/">版本控制系统</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p><img src="http://59caifu.com/d/file/2016-10-24/ef7
e216ba6fcb95732d8d3aaa065e78c.png" alt="wKioL1ehpxDzS1PRAADgEdjw0so255.png"></p>
<p><strong> git 现在的火爆程度非同一般，它被广泛地用在大型开源项目中，但是初学者非常容易“从入门到放弃”，各种命令各种参数，天哪，宝宝要吓哭了。实际上新手并不需要了解所有命令的用途 </strong> <strong> ，学习是需要一个循序渐进的过程，你可以从强大的命令开始。这个是给新手或熟悉图形工具的老鸟们看的教程，“从入门到放弃” </strong></p>
<p><strong> 一、基本了解 </strong></p>
<p>git命令是一些命令行工具的集合，它可以用来跟踪，记录文件的变动。比如你可以进行保存，比对，分析，合并等等。这个过程被称之为版本控制。已经有一系列的版本控制<br>系统，比如SVN, Mercurial, Perforce, CVS, Bitkeepe等等。</p>
<p>Git是分布式的，这意味着它并不依赖于中心服务器，任何一台机器都可以有一个本地版本的控制系统，我们称之为仓库。如果是多人协作的话，你需要还需要一个线上仓库，<br>用来同步信息，这就是GitHub, BitBucket的工作。</p>
<p><strong> 1.安装Git </strong></p>
<p>安装git非常直接,Linux - 打开控制台，然后通过包管理安装，在Ubuntu上命令是：</p>
<pre><code>sudo apt-get install git-all
</code></pre><p>Windows - 推荐使用git for windows，它包括了图形工具以及命令行模拟器。</p>
<p>OS X - 最简单的方式是使用homebrew安装，命令行执行</p>
<pre><code>&gt;brew install git
</code></pre><p>如果你是在是先用图形工具的话，那么推荐你使用Github desktop,Sourcetree。但我还是推荐你使用命令行，下面的内容就都是命令行的。</p>
<p><strong> 2.配置Git </strong></p>
<p>安装完git,首要任务是配置我们的信息，最重要的是用户名及邮箱，打开终端，执行以下命令。</p>
<pre><code>$ git config --global user.name &quot;My Name&quot;  $ git config --global user.email myEmail@example.com 
</code></pre><p>配置好这两项，用户就能知道谁做了什么，并且一切都更有组织性了不是吗?</p>
<p><strong> 3.创建一个新仓库 - git init </strong></p>
<p>git 会把所有文件以及历史记录保存在你的项目中，创建一个新的仓库，首先要去到项目路径，执行 git<br>init。然后git会创建一个隐藏的文件夹.git，所有的信息都储存在其中。</p>
<p>在桌面创建一个联系文件夹 git_exercise, 打开终端：</p>
<pre><code>$ cd Desktop/git_exercise/  $ git init 
</code></pre><p>OK，现在项目还什么都没有，新建一个 hello.txt 文件试试~</p>
<p><strong> 4.检查状态 - git status </strong></p>
<p>git status 是另一个非常重要的命令，它会告诉我们创库的当前状态：是否为最新代码，有什么更新等等执行git status:</p>
<pre><code>$ git status    On branch master    Initial commit    Untracked files:    (use &quot;git add ...&quot; to include in what will be committed)    hello.txt 
</code></pre><p>git<br>告诉我们，hello.txt尚未跟踪，这是因为这个文件是新的，git不知道是应该跟踪它的变动呢，还是直接忽略不管呢。为了跟踪我们的新文件，我们需要暂存它。</p>
<p><strong> 5.暂存 - git add </strong></p>
<p>git 有个概念叫 暂存区，你可以把它看成一块空白帆布，包裹着所有你可能会提交的变动。它一开始为空，你可以通过 git add 命令添加内容，并使用 git<br>commit 提交。</p>
<p>这个例子中只有一个文件：</p>
<pre><code>$ git add hello.txt
</code></pre><p>如果需要提交目录下的所有内容，可以这样：</p>
<pre><code>$ git add -A
</code></pre><p>再次使用git status查看：</p>
<pre><code>$ git status   On branch master   Initial commit   Changes to be committed:   (use &quot;git rm --cached ...&quot; to unstage)    new file: hello.txt&lt;br&gt;&lt;/span&gt; 
</code></pre><p>我们的文件已经提交了。状态信息还会告诉我们暂存区文件发生了什么变动，不过这里我们提交的是一个全新文件。</p>
<p><strong> 6.提交 - git commit </strong></p>
<p>一次提交代表着我们的仓库到了一个交付状态，通常是完成了某一块小功能。它就像是一个快照，允许我们像使用时光机一样回到旧时光。</p>
<p>创建提交，需要我们提交东西到暂存区(git add)，然后：</p>
<pre><code>$ git commit -m &quot;Initial commit.&quot;
</code></pre><p>这就创建了一次提交，-m “Initial commit.”表示对这次提交的描述，建议使用有意义的描述性信息。</p>
<p><strong> 二、远端仓库 </strong></p>
<p>到目前为止，我们的操作都是在本地的，它存在于.git文件中。为了能够协同开发，我们需要把代码发布到远端仓库上。</p>
<p><strong> 1.链接远端仓库 - git remote add </strong></p>
<p>为了能够上传到远端仓库，我们需要先建立起链接，这篇教程中，远端仓库的地址为：<a href="https://github.com/tutorialzine/awesome-" target="_blank" rel="noopener">https://github.com/tutorialzine/awesome-</a><br>project,但你应该自己在Github, BitBucket上搭建仓库，自己一步一步尝试。</p>
<p>添加测试用的远端仓库</p>
<pre><code>$ git remote add origin https://github.com/tutorialzine/awesome-project.git
</code></pre><p>一个项目可以同时拥有好几个远端仓库为了能够区分，通常会起不同的名字，通常主远端仓库被称为origin。</p>
<p><strong> 2.上传到服务器 - git push </strong></p>
<p>每次我们要提交代码到服务器上时，都会使用到git push。 git push命令会有两个参数，远端仓库的名字，以及分支的名字：</p>
<pre><code>$ git push origin master   Counting objects:  done.  Writing objects: 100% (3/3), 212 bytes | 0 bytes/s, done.  Total 3 (delta 0), reused 0 (delta 0)To * [new branch] master -&gt; master 
</code></pre><p>取决于你使用的服务器，push过程你可能需要验证身份。如果没有出差错，现在使用浏览器去你的远端分支上看，hello.txt已经在那里等着你了。</p>
<p><strong> 3.克隆仓库 - git clone </strong></p>
<p>放在Github上的开源项目，人们可以看到你的代码。可以使用 git clone进行下载到本地。</p>
<pre><code>$ git clone https://github.com/tutorialzine/awesome-project.git 
</code></pre><p>本地也会创建一个新的仓库，并自动将github上的分支设为远端分支。</p>
<p><strong> 4.从服务器上拉取代码 - git pull </strong></p>
<p>如果你更新了代码到仓库上，其他人可以通过git pull命令拉取你的变动：</p>
<pre><code>$ git pull origin master    From    * branch master -&gt; FETCH_HEAD    Already up-to-date. 
</code></pre><p>因为暂时没有其他人提交，所有没有任何变动</p>
<p><strong> 三、分支 </strong></p>
<p><img src="http://59caifu.com/d/file/2016-10-24/739
75b7db41d4d886c62cd1d1e839508.jpg" alt="wKioL1ehqO3zI9SHAABj35ny7Mk776.jpg"></p>
<p>当你在做一个新功能的时候，最好是在一个独立的区域上开发，通常称之为分支。分支之间相互独立，并且拥有自己的历史记录。这样做的原因是：</p>
<p>稳定版本的代码不会被破坏</p>
<p>不同的功能可以由不同开发者同时开发。</p>
<p>开发者可以专注于自己的分支，不用担心被其他人破坏了环境</p>
<p>在不确定之前，同一个特性可以拥有几个版本，便于比较</p>
<p><strong> 1.创建新分支 - git branch </strong></p>
<p>每一个仓库的默认分支都叫master, 创建新分支可以这样：</p>
<pre><code>$ git branch amazing_new_feature 
</code></pre><p>创建了一个名为amazing_new_feature的新分支，它跟当前分支同一起点</p>
<p><strong> 2.切换分支 - git checkout </strong></p>
<p>单独使用git branch，可以查看分支状态：</p>
<pre><code>$ git branch amazing_new_feature * master
</code></pre><ul>
<li>号表示当前活跃分支为master，使用git checkout切换分支。 </li>
</ul>
<pre><code>$ git checkout amazing_new_feature 
</code></pre><p><strong> 3.合并分支 - git merge </strong></p>
<p>我们的 amazing_new_feature 分支的任务是增加一个featuer.txt。我们来创建，添加到暂存区，提交。</p>
<pre><code>$ git add feature.txt    $ git commit -m &quot;New feature complete.&quot; 
</code></pre><p>新分支任务完成了，回到master分支</p>
<pre><code>$ git checkout master 
</code></pre><p>现在去查看文件，你会发现，之前创建的feature.txt文件不见了，因为master分支上并没有feature.txt。使用git merge 把<br>amazing_new_feature 分支合并到master上。</p>
<pre><code>$ git merge amazing_new_feature 
</code></pre><p>ok!然后再把amazing_new_feature 分支删掉吧。</p>
<pre><code>$ git branch -d amazing_new_feature 
</code></pre><p><strong> 四、高级 </strong></p>
<p>下面我们来说些比较高级并且使用的技巧。</p>
<p><strong> 1.比对两个不同提交之间的差别 </strong></p>
<p>每次提交都有一个唯一id，查看所有提交和他们的id，可以使用 git log:</p>
<pre><code>$ git log commit ba25c0ff30e1b2f0259157b42b9f8f5d174d80d7    Author: Tutorialzine   Date: Mon May 30 17:15:28 2016 +0300   New feature complete   commit b10cc1238e355c02a044ef9f9860811ff605c9b4   Author: Tutorialzine   Date: Mon May 30 16:30:04 2016 +0300   Added content to hello.txt   commit 09bd8cc171d7084e78e4d118a2346b7487dca059   Author: Tutorialzine&lt;br&gt;Date: Sat May 28 17:52:14 2016 +0300   Initial commit 
</code></pre><p>id 很长，但是你并不需要复制整个字符串，前一小部分就够了。</p>
<p>查看某一次提交更新了什么，使用 git show：</p>
<pre><code>$ git show b10cc123   \\commit b10cc1238e355c02a044ef9f9860811ff605c9b4   Author: Tutorialzine   Date: Mon May 30 16:30:04 2016 +0300   Added content to hello.txt   diff --git a/hello.txt b/hello.txt   index e69de29..b546a21 100644   --- a/hello.txt   +++ b/hello.txt   @@ -0,0 +1@@   +Nice weather today, isn&apos;t it? 
</code></pre><p>查看两次提交的不同，可以使用git diff [commit-from]..[commit-to] 语法：</p>
<pre><code>$ git diff 09bd8cc..ba25c0ff        diff --git a/feature.txt b/feature.txt   new file mode 100644        index 0000000..e69de29        diff --git a/hello.txt b/hello.txt        index e69de29..b546a21 100644        --- a/hello.txt        +++ b/hello.txt        @@-0,0 +1@@&lt;br&gt;+Nice weather today, isn&apos;t it? 
</code></pre><p>比较首次提交和最后一次提交，我们可以看到所有的更改。当然使用git difftool命令更加方便。</p>
<p><strong> 2.回滚某个文件到之前的版本 </strong></p>
<p>git 允许我们将某个特定的文件回滚到特定的提交，使用的也是 git checkout。</p>
<p>下面的例子，我们将hello.txt回滚到最初的状态，需要指定回滚到哪个提交，以及文件的全路径。</p>
<p>$ git checkout 09bd8cc1 hello.txt</p>
<p><strong> 3.回滚提交 </strong></p>
<p>如果你发现最新的一次提交完了加某个文件，你可以通过 git commit —amend来修复，它会把最新的提交打回暂存区，并尝试重新提交。</p>
<p>如果是更复杂的情况，比如不是最新的提交了。那你可以使用git revert。</p>
<p>最新的一次提交别名也叫HEAD。</p>
<pre><code>$ git revert HEAD 
</code></pre><p>其他提交可以使用id：</p>
<pre><code>$ git revert b10cc123 
</code></pre><p>混滚提交时，发生冲突是非常频繁的。当文件被后面的提交修改了以后，git不能正确回滚。</p>
<p><strong> 4.解决合并冲突 </strong></p>
<p>冲突经常出现在合并分支或者是拉去别人的代码。有些时候git能自动处理冲突，但大部分需要我们手动处理。</p>
<p>比如John 和 Tim 分别在各自的分支上写了两部分代码。</p>
<p>John 喜欢 for:</p>
<pre><code>// Use a for loop to console.log contents.    for(var i=0; i&lt;arr.length; i++) {    console.log(arr[i]);    } 
</code></pre><p>Tim 喜欢 forEach:</p>
<pre><code>// Use forEach to console.log contents.    arr.forEach(function(item) {    console.log(item);    }); 
</code></pre><p>假设John 现在去拉取 Tim的代码：</p>
<pre><code>$ git merge tim_branch  Auto-merging print_array.js  CONFLICT (content): Merge conflict in print_array.js  Automatic merge failed; fix conflicts and then commit the result. 
</code></pre><p>这时候git并不知道如何解决冲突，因为他不知道John和Tim谁写得更好。</p>
<p>于是它就在代码中插入标记。</p>
<pre><code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD  // Use a for loop to console.log contents.  for(var i=0; i&lt;arr.length; i++) {  console.log(arr[i]);   }   =======   // Use forEach to console.log contents.  arr.forEach(function(item) {  console.log(item);  });  &gt;&gt;&gt;&gt;&gt;&gt;&gt;; Tim s commit.
</code></pre><p>==== 号上方是当前最新一次提交，下方是冲突的代码。我们需要解决这样的冲突，经过组委会成员讨论，一致认定，在座的各位都是垃圾!两个都不要。改成下面的代码。</p>
<pre><code>// Not using for loop or forEach.    // Use Array.toString() to console.log contents.console.log(arr.toString()); 
</code></pre><p>好了，再提交一下：</p>
<pre><code>$ git add -A $ git commit -m &quot;Array printing conflict resolved.&quot;
</code></pre><p>如果在大型项目中，这个过程可能容易出问题。你可以使用GUI 工具来帮助你。使用 git mergetool。</p>
<p><strong> 5.配置 .gitignore </strong></p>
<p>大部分项目中，会有写文件，文件夹是我们不想提交的。为了防止一不小心提交，我们需要gitignore文件：</p>
<p>在项目根目录创建.gitignore文件</p>
<p>在文件中列出不需要提交的文件名，文件夹名，每个一行</p>
<p>.gitignore文件需要提交，就像普通文件一样</p>
<p>通常会被ignore的文件有：</p>
<pre><code>log文件  task runner builds  node_modules等文件夹  IDEs生成的文件  个人笔记 
</code></pre><p>例如：</p>
<pre><code>*.log  build/  node_modules/  .idea/  my_notes.txt 
</code></pre>
        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/Git命令“从入门到放弃”/#comments" id="sourceId::2018/04/20/Git命令“从入门到放弃”/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-Ext JS 6学习文档-第3章-基础组件" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/Ext JS 6学习文档-第3章-基础组件/">Ext JS 6学习文档-第3章-基础组件</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/Ext JS 6学习文档-第3章-基础组件/">
            <time datetime="2018-04-19T16:34:21.418Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ExtJs/">ExtJs</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/ext/">ext</a>, <a class="tag-link" href="/tags/extjs/">extjs</a>, <a class="tag-link" href="/tags/java/">java</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>基础组件</p>
<p>在本章中，你将学习到一些 Ext JS 基础组件的使用。同时我们会结合所学创建一个小项目。这一章我们将学习以下知识点：</p>
<ul>
<li>熟悉基本的组件 – 按钮，文本框，日期选择器等等 </li>
<li>表单字段的校验 </li>
<li>菜单和工具栏 </li>
<li>设计一个表单 </li>
<li>计算器程序– 本章的示例项目   </li>
</ul>
<p>本章的主要目的是创建一个表单设计和一个计算器示例项目。以下图分别展示了表单设计和计算器设计。</p>
<p>首先，你观察下列表单设计，你会发现我们使用了大量的控件，例如 label 和文本框。</p>
<p>以下图展示了表单的设计：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/124.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/124.jpg" alt="1">
</a></p>
<p>继续，设计计算器程序大量的使用了按钮控件。所以你首要学习的是按钮和 handler 。随后在本章最后我们将会构建一个<br>计算器程序。在这个过程中，你会知道如何使 view(视图) 和 controller(控制器)进行交互并协同工作。我们还将看到如何绑定 view<br>model(视图模型) 的属性到一个 view(视图) 的字段上。</p>
<p>下图为计算机的设计展示：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/125.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/125.jpg" alt="1">
</a></p>
<h2 id="熟悉基本组件"><a href="#熟悉基本组件" class="headerlink" title="熟悉基本组件"></a>熟悉基本组件</h2><p>Ext JS 有大量的优秀的控件，现在让我们开始认识这些基础的组件吧。</p>
<h2 id="Ext-Button"><a href="#Ext-Button" class="headerlink" title="Ext.Button"></a>Ext.Button</h2><p>这是一个很常用的控件；handler 是用于处理单击事件，如以下代码所示：</p>
<pre><code>Ext.create(&apos;Ext.Button&apos;, {   
    text: &apos;My Button&apos;,   
    renderTo: Ext.getBody(),   
    handler: function() {     
        alert(&apos;click&apos;);
    }
});
</code></pre><p>前面代码的输出：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/126.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/126.jpg" alt="1">
</a></p>
<p>我在第二章已经介绍过如何运行样例代码，但这里我还想再次重申这一点，此文档中的大部分样例代码都是可以直接运行的。你可以选择在你本地设备上或者在 Sencha<br>Fiddle 上执行这些示例代码。你可以访问  Sencha Fiddle  并将上面的代码键入到 launch 函数中，运行并查看结果。如果你访问了  <a href="https://fiddle.sencha.com/" target="_blank" rel="noopener"><br>https://fiddle.sencha.com </a> 将会看到下列代码：</p>
<pre><code>Ext.application({   
    name : &apos;Fiddle&apos;,   
    launch : function() {
        Ext.Msg.alert(&apos;Fiddle&apos;, &apos;Welcome to Sencha Fiddle!&apos;);
    }
}) ;
</code></pre><p>现在粘贴下列的创建按钮的样例代码，运行并查看结果：</p>
<pre><code>Ext.application({   
    name : &apos;Fiddle&apos;,   
    launch : function() {
        Ext.create(&apos;Ext.Button&apos;, {       
            text: &apos;My Button&apos;,       
            renderTo: Ext.getBody(),       
            handler: function() {         
                alert(&apos;click&apos;);
            }
        });
    }
});
</code></pre><ul>
<li>不是所有的代码都可以这样运行，此外并非所有的示例代码都会有视觉呈现。 </li>
</ul>
<p>你还可以使用  listeners  配置添加更多的事件处理器，如以下代码所示：</p>
<pre><code>Ext.create(&apos;Ext.Button&apos;, {   
    text: &apos;My Button&apos;,   
    renderTo: Ext.getBody(),   
    listeners: {     
        click: {       
            fn: function(){
                //Handle click event         
                alert(&apos;click&apos;);
            }
        },
        mouseout: {       
            fn: function(){
                //Handle double click event         
                alert(&apos;Mouse out&apos;);
             }
        }
    }
});
</code></pre><p>以上代码只是创建了一个简单的按钮，你还可以创建很多不同的按钮，有 link button(连接按钮)，menu button(菜单按钮)，toggle<br>button(开关按钮) 等等；</p>
<p>来创建一个链接按钮，设置  href  属性，如以下代码所示：</p>
<pre><code>Ext.create(&apos;Ext.Button&apos;, {   
    renderTo: Ext.getBody(),   
    text: &apos;Link Button&apos;,   
    href: &apos;http://www.sencha.com/&apos; 
});
</code></pre><p>上面创建的链接按钮输出如图。当点击它则打开链接：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/127.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/127.jpg" alt="1">
</a></p>
<p>通过设置  menu  属性，创建一个菜单按钮，如以下代码所示：</p>
<pre><code>Ext.create(&apos;Ext.Button&apos;, {   
    text: &apos;My Button&apos;,   
    renderTo: Ext.getBody(),   
    menu: [{     
        text: &apos;Item 1&apos;
    }, {     
        text: &apos;Item 2&apos;
    }, {     
        text: &apos;Item 3&apos;
    }]
});
</code></pre><p>输出如下，当点击时出现下拉菜单：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/128.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/128.jpg" alt="1">
</a></p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/129.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/129.jpg" alt="1">
</a></p>
<p>Ext.Button  还有许多属性，例如  bind  ，  cls  ，  disabled  ，  html  ，  tooltip  ，  tpl<br>等等，你可以根据自己需求使用。</p>
<h2 id="Ext-MessageBox"><a href="#Ext-MessageBox" class="headerlink" title="Ext.MessageBox"></a>Ext.MessageBox</h2><p>Ext.window.MessageBox  类提供了 message box 实现。  Ext. MessageBox  是一个单例对象。你可以使用<br>MessageBox  弹出一个警告，信息确认，提示输入等等。</p>
<p>下列代码将弹出一个简单的提示信息。这里解释一下  Ext.Msg  是  Ext. Messagebox  类的别名：</p>
<pre><code>Ext.Msg.alert(&apos;Info&apos;, &apos;Document saved!&apos;);
</code></pre><p>下列代码将弹出一个消息确认框，button 为选择的值，取 yes 或 no ：</p>
<pre><code>Ext.Msg.confirm(&apos;Confirm&apos;, &apos;Are you want to cancel the updates?&apos;, function(button){   
    if(&apos;yes&apos;==button) {

    }   else {

    }
});
</code></pre><p>你也可以自定义这个 message box 如下：</p>
<pre><code>Ext.MessageBox.show({   
    title:&apos;Save Changes?&apos;,   
    msg: &apos;Do you want to save the file?&apos;,   
    buttons: Ext.MessageBox.YESNO,   
    fn: function(button){     
        if(&apos;yes&apos;==button){

        }else if(&apos;no&apos;==button){

        }   
    },
    icon: Ext.MessageBox.QUESTION 
}) ;
</code></pre><p>上面代码输出如下：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/130.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/130.jpg" alt="1">
</a></p>
<p>表单和表单字段</p>
<p>现在我们看一下都有哪些表单相关的组件。</p>
<h2 id="Ext-form-Panel"><a href="#Ext-form-Panel" class="headerlink" title="Ext.form.Panel"></a>Ext.form.Panel</h2><p>这个 form panel (表单面板)继承自 panel  并添加了表单相关的功能，例如字段管理，校验，提交等等。form panel 的默认布局是<br>anchor layout ，但是如果需要你可以改变这个配置。</p>
<p>form panel 有一个很方便的配置为  fieldDefaults  ，它可以用于指定表单内所有字段的默认类型。</p>
<h2 id="fields-字段-表单域"><a href="#fields-字段-表单域" class="headerlink" title="fields (字段/表单域)"></a>fields (字段/表单域)</h2><p>Ext JS 提供了很多内置的表单字段。比较常用的一些字段：</p>
<pre><code>Ext.form.field.Checkbox

Ext.form.field.ComboBox

Ext.form.field.Date

Ext.form.field.File

Ext.form.field.Hidden

Ext.form.field.HtmlEditor

Ext.form.field.Number

Ext.form.field.Radio

Ext.form.field.Text

Ext.form.field.TextArea

Ext.form.field.Time
</code></pre><p>我们看一下其中的一些字段的应用。</p>
<h2 id="Ext-form-field-Text"><a href="#Ext-form-field-Text" class="headerlink" title="Ext.form.field.Text"></a>Ext.form.field.Text</h2><p>这是一个基本的文本框，它具有很多有用的属性和配置。其中有一个很有用的属性是  vtype  它是用于校验的。 例如以下代码，这个  vtype  属性为<br>email 用于验证输入内容是否是有效的电子邮箱：</p>
<pre><code>Ext.create(&apos;Ext.form.field.Text&apos;, {   
    renderTo: Ext.getBody(),   
    name: &apos;email&apos;,   
    fieldLabel: &apos;Email&apos;,   
    allowBlank: false,   
    vtype: &apos;email&apos; 
});
</code></pre><p>这里  allowBlank  也是一个校验属性。通过设置 allowBlank 属性为 false ，如果这个字段为空白，将会提示校验不通过。</p>
<h2 id="Ext-form-field-Number"><a href="#Ext-form-field-Number" class="headerlink" title="Ext.form.field.Number"></a>Ext.form.field.Number</h2><p>number 字段继承自 spinner 字段，spinner 字段则继承自 text 字段，进而的 number 等于是继承了两者。这个 number<br>字段提供了几个选项来处理数值。下列代码创建了一个数值文本框：</p>
<pre><code>Ext.create(&apos;Ext.form.field.Number&apos;, {   
    renderTo: Ext.getBody(),   
    name: &apos;Count&apos;,   
    fieldLabel: &apos;Count&apos;,
    value: 0,   
    maxValue: 10,   
    minValue: 0 
});
</code></pre><p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/131.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/131.jpg" alt="1">
</a></p>
<p>你可以移除下拉按钮，方向键，鼠标滚轮监听，用配置：  hideTrigger  ，  keyNavEnabled  ，和<br>mouseWheelEnabled  。</p>
<h2 id="Ext-form-field-ComboBox"><a href="#Ext-form-field-ComboBox" class="headerlink" title="Ext.form.field.ComboBox"></a>Ext.form.field.ComboBox</h2><p>下列代码创建了一个月份下拉菜单。这个  combobox  有一个配置为  store  。 这个  store  是数据源，为此下拉菜单提供数据。<br>store  是属于 ExtJS 中数据包部分， 在接下来的章节中我们会详细介绍的。</p>
<p>combobox  中另一个重要的配置是  queryMode  。这个属性取值可以是 ‘local’ 或者 ‘remote’。如果你设置为 remote<br>了，那么这个数据源 store 将在运行加载数据时发送请求从远程服务器获取数据：</p>
<pre><code>var months = Ext.create(&apos;Ext.data.Store&apos;, {
    fields: [&apos;abbr&apos;, &apos;name&apos;],
    data: [{&quot;abbr&quot;:&quot;JAN&quot;, &quot;name&quot;:&quot;January&quot;},
        {&quot;abbr&quot;:&quot;FEB&quot;, &quot;name&quot;:&quot;February&quot;},   
        {&quot;abbr&quot;:&quot;MAR&quot;, &quot;name&quot;:&quot;March&quot;},
        {&quot;abbr&quot;:&quot;APR&quot;, &quot;name&quot;:&quot;April&quot;},   
        {&quot;abbr&quot;:&quot;MAY&quot;, &quot;name&quot;:&quot;May&quot;},
        {&quot;abbr&quot;:&quot;JUN&quot;, &quot;name&quot;:&quot;June&quot;},   
        {&quot;abbr&quot;:&quot;JUL&quot;, &quot;name&quot;:&quot;July&quot;},
        {&quot;abbr&quot;:&quot;AUG&quot;, &quot;name&quot;:&quot;August&quot;},
        {&quot;abbr&quot;:&quot;SEP&quot;, &quot;name&quot;:&quot;September&quot;},   
        {&quot;abbr&quot;:&quot;OCT&quot;, &quot;name&quot;:&quot;October&quot;},
        {&quot;abbr&quot;:&quot;NOV&quot;, &quot;name&quot;:&quot;November&quot;},
        {&quot;abbr&quot;:&quot;DEC&quot;, &quot;name&quot;:&quot;December&quot;}] 
}) ;

Ext.create(&apos;Ext.form.ComboBox&apos;, {   
    fieldLabel: &apos;Choose Month&apos;,
    store: months,   
    queryMode: &apos;local&apos;,   
    displayField: &apos;name&apos;,
    valueField: &apos;abbr&apos;,   
    renderTo: Ext.getBody() 
});
</code></pre><p>以上代码的输出如下：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/132.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/132.jpg" alt="1">
</a></p>
<h2 id="Ext-form-field-HtmlEditor"><a href="#Ext-form-field-HtmlEditor" class="headerlink" title="Ext.form.field.HtmlEditor"></a>Ext.form.field.HtmlEditor</h2><p>Ext JS 也有一个非常优秀的 HTML 编辑器，它提供直接在 web 页面上处理文字的能力，如以下代码所示：</p>
<pre><code>Ext.create(&apos;Ext.form.HtmlEditor&apos;, {   
    width: 800,   
    height: 200,   
    renderTo: Ext.getBody() 
});
</code></pre><p>以上代码输出如下：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/133.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/133.jpg" alt="1">
</a></p>
<h2 id="表单字段的校验"><a href="#表单字段的校验" class="headerlink" title="表单字段的校验"></a>表单字段的校验</h2><p>大多数表单都有自己的校验规则，例如你键入了一个非数值的内容到 number 字段，它将显示一个验证无效的提示。再有这个 text 字段(文本框) 校验属性有<br>allowBlank  ，  minLength  ，和  maxLength  。 更进一步的，还有  regex  属性可以使用正则表达式自定义校验。</p>
<h2 id="form-panel-的事件"><a href="#form-panel-的事件" class="headerlink" title="form panel 的事件"></a>form panel 的事件</h2><p>form panel 支持的部分事件：</p>
<ul>
<li>beforeaction  : 任意动作执行前触发，例如 submit，load，doAction 这些动作执行时 </li>
<li>actionfailed  : 执行一个动作失败时触发 </li>
<li>actioncomplete  : 在一个动作执行完成之后触发This event will be fired after an action is completed </li>
<li>validitychange  : 表单键入的内容有效性发生变化时触发 </li>
<li>dirtychange  : 表单的dirty状态改变时触发 </li>
</ul>
<h2 id="表单字段容器"><a href="#表单字段容器" class="headerlink" title="表单字段容器"></a>表单字段容器</h2><p>以下是一些 from panel 里很有用的容器。</p>
<h2 id="Ext-form-CheckboxGroup"><a href="#Ext-form-CheckboxGroup" class="headerlink" title="Ext.form.CheckboxGroup"></a>Ext.form.CheckboxGroup</h2><p>CheckboxGroup  继承自  FieldContainer  用于组织复选框。下列示例中，复选框组的 items 中所有的项都有相同的 name<br>；这有助于将得到的值作为一个单一的参数传递给服务器。</p>
<pre><code>Ext.create(&apos;Ext.form.CheckboxGroup&apos;, {   
    renderTo: Ext.getBody(),   
    fieldLabel: &apos;Skills &apos;,   
    vertical: true,   
    columns: 1,   
    items: [{ boxLabel: &apos;C++&apos;, name: &apos;rb&apos;, inputValue: &apos;1&apos; },
        { boxLabel: &apos;.Net Framework&apos;, name: &apos;rb&apos;, inputValue: &apos;2&apos;, checked: true },
        { boxLabel: &apos;C#&apos;, name: &apos;rb&apos;, inputValue: &apos;3&apos; },
        { boxLabel: &apos;SQL Server&apos;, name: &apos;rb&apos;, inputValue: &apos;4&apos; }]
}) ;
</code></pre><p>以上代码输出如下：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/134.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/134.jpg" alt="1">
</a></p>
<h2 id="Ext-form-FieldContainer"><a href="#Ext-form-FieldContainer" class="headerlink" title="Ext.form.FieldContainer"></a>Ext.form.FieldContainer</h2><p>FieldContainer  是很有用的，当你想将一组相关字段附加到一个标签时。</p>
<p>以下代码的输出你会发现一个 label 后面绑定了两个文本框：</p>
<pre><code>Ext.create(&apos;Ext.form.FieldContainer&apos;, {
    renderTo: Ext.getBody(), 
    fieldLabel: &apos;Name&apos;,   
    layout: &apos;hbox&apos;,   
    combineErrors: true,   
    defaultType: &apos;textfield&apos;,
    defaults: {     
        hideLabel: &apos;true&apos;
    },   
    items: [{
        name: &apos;firstName&apos;,
        fieldLabel: &apos;First Name&apos;,
        flex: 2,     
        emptyText: &apos;First&apos;,     
        allowBlank: false
    }, {     
        name: &apos;lastName&apos;,     
        fieldLabel: &apos;Last Name&apos;,
        flex: 3,     
        margin: &apos;0 0 0 6&apos;,     
        emptyText: &apos;Last&apos;,     
        allowBlank: false
    }]
});
</code></pre><p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/1-5.png" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/1-5.png" alt="">
</a></p>
<h2 id="Ext-form-RadioGroup"><a href="#Ext-form-RadioGroup" class="headerlink" title="Ext.form.RadioGroup"></a>Ext.form.RadioGroup</h2><p>RadioGroup  继承自  CheckboxGroup  用于组织单选按钮。items 中的项都有相同的 name，另外这是单选的，如以下代码所示：</p>
<pre><code>Ext.create(&apos;Ext.form.RadioGroup&apos;, {   
    renderTo: Ext.getBody(),   
    fieldLabel: &apos;Sex &apos;,   
    vertical: true,   
    columns: 1,   
    items: [{ 
        boxLabel: &apos;Male&apos;, 
        name: &apos;rb&apos;, 
        inputValue: &apos;1&apos; 
    },{ 
        boxLabel: &apos;Female&apos;, 
        name: &apos;rb&apos;, 
        inputValue: &apos;2&apos; 
    }]
});
</code></pre><p>代码输出：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/136.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/136.jpg" alt="1">
</a></p>
<h2 id="提交表单"><a href="#提交表单" class="headerlink" title="提交表单"></a>提交表单</h2><p>使用 form 的 submit 方法提交表单。使用  getForm  方法获取表单并  isValid  方法进行提交前的表单内容校验。如以下代码所示：</p>
<pre><code>var form = this.up(&apos;form&apos;).getForm();
if (form.isValid()) {   
    form.submit({     
        url: &apos;someurl&apos;,     
        success: function () {

        },
        failure: function () {

        }
    });
} else {
    Ext.Msg.alert(&apos;Error&apos;, &apos;Fix the errors in the form&apos;)
}
</code></pre><h2 id="菜单和工具栏"><a href="#菜单和工具栏" class="headerlink" title="菜单和工具栏"></a>菜单和工具栏</h2><p>对于你能想到的任何的菜单和工具栏 Ext JS 提供了最完整的支持。  Ext.toolbar.Toolbar  用于构建一个工具栏。默认情况下任何子项在<br>Ext.toolbar.Toolbar  都是按钮，但是你可以添加任意控件进去，例如一个文本框，一个数值框，一个图标，一个下拉菜单等等。</p>
<p>规范整理你的工具栏中的项，你可以使用 空格(  Ext.toolbar.Spacer  )， 分隔符(  Ext.toolbar. Separator<br>)，和 使控件右对齐(  Ext.toolbar.Fill  ) 。这里也可以使用快捷方式  ‘ ‘ (空格)，’-‘ 和 ‘|’<br>(都是分隔符，只有很小的差别)，和 ‘  -&gt; ‘ (右对齐)。</p>
<p>Ext.menu.Menu  用于构建一个菜单，items 属性中为  Ext.menu.Item  一个个菜单项。</p>
<p>一个简单的代码示例和以下截图的输出：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/137.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/137.jpg" alt="1">
</a></p>
<p>Ext  .  create  (  ‘Ext.toolbar.Toolbar’  ,  {</p>
<p>renderTo  :  Ext  .  getBody  (  )  ,</p>
<p>width  :  800  ,  items  :  [  {</p>
<p>text  :  ‘My Button’</p>
<p>}  ,  {</p>
<p>text  :  ‘My Button’  ,</p>
<p>menu  :  [  {</p>
<p>text  :  ‘Item 1’</p>
<p>}  ,  {</p>
<p>text  :  ‘Item 2’</p>
<p>}  ,  {</p>
<p>text  :  ‘Item 3’</p>
<p>}  ]</p>
<p>}  ,  {</p>
<p>text  :  ‘Menu with divider’  ,</p>
<p>tooltip  :  {</p>
<p>text  :  ‘Tooltip info’  ,</p>
<p>title  :  ‘Tip Title’</p>
<p>}  ,</p>
<p>menu  :  {</p>
<p>items  :  [  {</p>
<p>text  :  ‘Task 1’  ,</p>
<p>// handler: onItemClick</p>
<p>}  ,  ‘-‘  ,  {</p>
<p>text  :  ‘Task 2’  ,</p>
<p>// handler: onItemClick</p>
<p>}  ,  {</p>
<p>text  :  ‘Task 3’  ,</p>
<p>// handler: onItemClick</p>
<p>}  ]</p>
<p>}</p>
<p>}  ,  ‘-&gt;’  ,  {</p>
<p>xtype  :  ‘textfield’  ,</p>
<p>name  :  ‘field1’  ,</p>
<p>emptyText  :  ‘search web site’</p>
<p>}  ,  ‘-‘  ,  ‘Some Info’  ,  {</p>
<p>xtype  :  ‘tbspacer’</p>
<p>}  ,  {</p>
<p>name  :  ‘Count’  ,</p>
<p>xtype  :  ‘numberfield’  ,</p>
<p>value  :  0  ,</p>
<p>maxValue  :  10  ,</p>
<p>minValue  :  0  ,</p>
<p>width  :  60</p>
<p>}  ]</p>
<p>}  )  ;</p>
<h2 id="设计一个-客户反馈-表单"><a href="#设计一个-客户反馈-表单" class="headerlink" title="设计一个(客户反馈)表单"></a>设计一个(客户反馈)表单</h2><p>现在根据之前所学，我们来设计一个表单。</p>
<p>我们将设计如图所示的表单：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/138.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/138.jpg" alt="1">
</a></p>
<p>以下是这个表单的代码。这里我维护着一个这个例子的完整的源码  <a href="https://github.com/ananddayalan/extjs-by-
example-customer-feedback-form" target="_blank" rel="noopener"> https://github.com/ananddayalan/extjs-by-<br>example-customer-feedback-form </a></p>
<p>这里我们所有的组件都在  Viewport  中。 这是一个专用的容器，它代表浏览器里应用的视图区域。</p>
<p>在  Viewport  中我们设置 scrollable 选项将子组件设为滚动的，使用 true 或 false 。也可以取值为 x 或 y<br>表示只允许水平或垂直滚动：</p>
<pre><code>Ext.create(&apos;Ext.container.Viewport&apos;, {
    scrollable: true,   
    items: [{     
        xtype: &apos;container&apos;,    
        layout: {   
            type: &apos;hbox&apos;,   
            align: &apos;center&apos;,   
            pack: &apos;center&apos;
        },     
        items: [ {       
            xtype: &apos;form&apos;,       
            bodyPadding: 20,       
            maxWidth: 700,       
            flex: 1,
            title: &apos;Custom Feedback&apos;,
            items:[{
                xtype: &apos;fieldcontainer&apos;,         
                layout: &apos;hbox&apos;,         
                fieldLabel: &apos;Name&apos;,         
                defaultType: &apos;textfield&apos;,
                defaults: {           
                    allowBlank: false,           
                    flex: 1
                },
                items: [{           
                    name: &apos;firstName&apos;,           
                    emptyText: &apos;First Name
                }, {          
                    name: &apos;lastName&apos;,           
                    margin: &apos;0 0 0 5&apos;,           
                    emptyText: &apos;Last Name&apos;
                }]
            },{         
                xtype: &apos;datefield&apos;,         
                fieldLabel: &apos;Date of Birth&apos;,
                name: &apos;dob&apos;,         
                maxValue: new Date() /* Prevent entering the future date.*/
            }, {
                fieldLabel: &apos;Email Address&apos;,         
                name: &apos;email&apos;, 
                vtype: &apos;email&apos;,
                allowBlank: false
            }, {
                fieldLabel: &apos;Phone Number&apos;,         
                labelWidth: 100,
                name: &apos;phone&apos;,
                width: 200, 
                emptyText: &apos;xxx-xxx-xxxx&apos;,
                maskRe: /[\d\-]/,         
                regex: /^\d{3}-\d{3}-\d{4}$/,
                regexText: &apos;The format must be xxx-xxx-xxxx&apos; 
            },{
                xtype: &apos;radiogroup&apos;,
                fieldLabel: &apos;How satisfied with our service?&apos;,
                vertical: true, 
                columns: 1,
                items: [ {           
                    boxLabel: &apos;Very satisfied&apos;,
                    name: &apos;rb&apos;,           
                    inputValue: &apos;1&apos;
                }, {
                    boxLabel: &apos;Satisfied&apos;,           
                    name: &apos;rb&apos;, inputValue: &apos;2&apos;
                }]       
            },{
                xtype: &apos;checkboxgroup&apos;,
                fieldLabel: &apos;Which of these words would you use to describe our products? Select all that apply&apos;,
                vertical: true,         
                columns: 1,         
                items: [{           
                    boxLabel: &apos;Reliable&apos;,          
                    name: &apos;ch&apos;,           
                    inputValue: &apos;1&apos;
                }]
            },{
                xtype: &apos;radiogroup&apos;,
                fieldLabel: &apos;How likely is it that you would recommend this company to a friend or colleague?&apos;,
                vertical: false,         
                defaults: { padding: 20 },
                items: [ {           
                    boxLabel: &apos;1&apos;,           
                    name: &apos;recommend&apos;,           
                    inputValue: &apos;1&apos;
                }],         
                buttons: [{
                    text: &apos;Submit&apos;,          
                    handler: function () {
                        var form = this.up(&apos;form&apos;).getForm();
                        if (form.isValid()) {               
                            form.submit({                 
                                url: &apos;cutomer/feedback&apos;,                 
                                success: function () {},                 
                                failure: function () {}
                            });
                        } else {
                            Ext.Msg.alert(&apos;Error&apos;, &apos;Fix the errors in the form&apos;)
                        }
                    }
                }]
            }]
        }]
    }]
});
</code></pre><p>在以上代码中通过在容器级设置  defaultType  属性，这样我们就可以不必在容器的每个子组件里重复的指定  xtype<br>属性了。这样默认情况下，所有子组件在没有显式指定  xtype  时默认的类型都是  textfield  。</p>
<p>form panel 上有一个 flex 配置用于填补父容器的宽度，同时通过设置 maxWidth 为 700 限制 form panel 的最大宽度。</p>
<p>字段容器使用 hbox 布局将 first name 和 last name 文本框放在一个 label 标签下。</p>
<h2 id="写一个计算器应用"><a href="#写一个计算器应用" class="headerlink" title="写一个计算器应用"></a>写一个计算器应用</h2><p>现在我们结合目前所学构建一个完整的小项目。这是我们将要构建的计算器的设计：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/125.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/125.jpg" alt="1">
</a></p>
<h2 id="文件夹结构"><a href="#文件夹结构" class="headerlink" title="文件夹结构"></a>文件夹结构</h2><p>这是我们创建的计算器工程的目录结构。这里我不是用 sencha Cmd 生成的项目，只是从 Ext JS 复制了一些必须的文件到项目文件夹中：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/139.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/139.jpg" alt="1">
</a></p>
<p>完整可用的项目在这里：  <a href="https://github.com/ananddayalan/extjs-by-example-calculator" target="_blank" rel="noopener"> https://github.com/ananddayalan/extjs-by-example-calculator
</a> .</p>
<h2 id="App-–-app-js"><a href="#App-–-app-js" class="headerlink" title="App – app.js"></a>App – app.js</h2><p>在  app.js  文件里我们简单的创建了  Main  视图，作为可移动窗体浮动在浏览器：</p>
<pre><code>Ext.application({   
    name: &apos;Calc&apos;,   
    launch: function () {
        Ext.create(&apos;Calc.view.main.Main&apos;).show();
    }
});
</code></pre><h2 id="再谈-MVC-和-MVVM"><a href="#再谈-MVC-和-MVVM" class="headerlink" title="再谈 MVC 和 MVVM"></a>再谈 MVC 和 MVVM</h2><p>第一章的时候，我们已经介绍过  MVC  (  Model View Controller  ) 和  MVVM  (  Model View<br>ViewModel  )。 这个示例项目的代码很好的展示了 视图，控制器，和视图模型之间的区别。</p>
<h2 id="Model-模型"><a href="#Model-模型" class="headerlink" title="Model (模型)"></a>Model (模型)</h2><p>这代表着数据层。model 保存的数据可以包含数据验证和逻辑。</p>
<h2 id="View-视图"><a href="#View-视图" class="headerlink" title="View (视图)"></a>View (视图)</h2><p>这一层是用户界面。包含有 button，form，和 message box 等等组件。在我们这次写的计算器应用中  main.js<br>就是一个很好的视图例子。</p>
<h2 id="Controller-控制器"><a href="#Controller-控制器" class="headerlink" title="Controller (控制器)"></a>Controller (控制器)</h2><p>控制器处理 view(视图)相关的逻辑，例如 view 的 event(事件)处理，还有任何程序相关逻辑都可以写在这里。</p>
<h2 id="ViewController-视图控制器-和-Controller-控制器"><a href="#ViewController-视图控制器-和-Controller-控制器" class="headerlink" title="ViewController (视图控制器) 和 Controller (控制器)"></a>ViewController (视图控制器) 和 Controller (控制器)</h2><p>在 Ext JS 5 和 6 中，有两种类型的控制器：  ViewController  和  Controller  。 这个<br>ViewController  自 Ext JS 5 开始引进的。  ViewController<br>是为一个指定的视图创建的控制器，但是这个控制器也可以交叉其他视图的逻辑。</p>
<p>ViewController  带来了一些新的概念，例如 引用和监听，简化视图与控制之间的关系。同时  View  销毁时  ViewController<br>也会被销毁，他们具有相同的生命周期，在这个例子中我们没有使用 引用和监听，但是在下一个例子中我们会使用的。</p>
<ul>
<li>你可以使用 listeners  代替 handler 处理事件 </li>
</ul>
<h2 id="View-model"><a href="#View-model" class="headerlink" title="View model"></a>View model</h2><p>view model 封装了 view(视图)所需要的展示逻辑，绑定数据到 view 并且每当数据改变时处理更新。</p>
<p>它有别于 model ，  view model  主要是为一个指定的视图而创建的。一个  model  是一个纯粹的数据类并可用于整个应用中，但一个<br>view model  是起到一个  view  和  model  之间的数据粘合剂的作用。看一下  main.js  的 视图模型绑定。</p>
<h2 id="视图-—-Main-js"><a href="#视图-—-Main-js" class="headerlink" title="视图 — Main.js"></a>视图 — Main.js</h2><p>这里我为这个计算器应用创建一个视图为  Main  。这个视图里包含所有的按钮，显示字段等等。相关的事件用 controller<br>的方法。这个视图的控制器已经使用 controller 配置指定了。</p>
<p>这个视图使用 table 布局，配置为 4 列。CSS 类使用  cls  属性指定。</p>
<p>代码里有附加的注释：</p>
<pre><code>Ext.define(&apos;Calc.view.main.Main&apos;, {   
    extend: &apos;Ext.window.Window&apos;,
    /* 表示在当前视图加载之前先加载这些所需的类*/
    requires: [ &apos;Calc.view.main.MainController&apos;,
                &apos;Calc.view.main.MainModel&apos;],   
    xtype: &apos;app-main&apos;,   
    controller: &apos;main&apos;,
    /* 视图的 view model (视图模型)*/   
    viewModel: { type: &apos;main&apos; },
    resizable: false,   
    layout: {     
        type: &apos;table&apos;,     
        columns: 4
    },  
    /* defaultType 和 defaults 属性是用于 items 内的子组件的，任何子组件都可以覆盖这些配置 */
    defaultType: &apos;button&apos;,   
    defaults: {     
        width: 50,
        height: 50,     
        cls: &apos;btn&apos;,     
        handler: &apos;onClickNumber&apos;
    },  
    /* 这里我用 Ext.window.Window 的 header 显示计算器的结果。使用 header 你可以在浏览器里移动这个计算器。*/
    header: {     
        items: [{            
            xtype: &apos;displayfield&apos;,       
            colspan: 4,       
             width: 200,      
            cls: &apos;display&apos;,       
             bind: { 
                 value: &apos;{display}&apos; 
             },
             height: 60,       
             padding: 0
        }]
    },
    items: [{     
        text: &apos;C&apos;,     
        colspan: 2,     
        width: 100,     
        cls: &apos;btn-green&apos;,     
        handler: &apos;onClickClear&apos;
    }, {     
        text: &apos;+/-&apos;,     
        cls: &apos;btn-green&apos;,     
        handler: &apos;onClickChangeSign&apos;
    }, {     
        text: &apos;÷&apos;,     
        cls: &apos;btn-orange&apos;,     
        handler: &apos;onClickOp&apos;
    },{ 
        text: &apos;7&apos; 
    },{ 
        text: &apos;8&apos; 
    },{ 
        text: &apos;9&apos; 
    },{ 
        text: &apos;×&apos;,     
        cls: &apos;btn-orange&apos;,     
        handler: &apos;onClickOp&apos;
    },{ 
        text: &apos;4&apos;
    },{ 
        text: &apos;5&apos;
    },{ 
        text: &apos;6&apos;
    },{ 
        text: &apos;-&apos;,     
        cls: &apos;btn-orange&apos;,     
        handler: &apos;onClickOp&apos;
    },{ 
        text: &apos;1&apos;
    },{ 
        text: &apos;2&apos;
    },{ 
        text: &apos;3&apos;
    },{ 
        text: &apos;+&apos;,     
        cls: &apos;btn-orange&apos;,     
        handler: &apos;onClickOp&apos;
    },{ 
        text: &apos;0&apos;,     
        width: 100,     
        colspan: 2
    },{ 
        text: &apos;.&apos;,     
        handler: &apos;onClickDot&apos;
    },{ 
        text: &apos;=&apos;,     
        cls: &apos;btn-orange&apos;,     
        handler: &apos;onClickOp&apos;
    }] 
});
</code></pre><h2 id="控制器-—-MainController-js"><a href="#控制器-—-MainController-js" class="headerlink" title="控制器 — MainController.js"></a>控制器 — MainController.js</h2><p>虽然这个控制器的代码有点长，这是一个非常简单的代码。控制器中有很多方法处理按钮的点击事件，例如运算符和操作数的点击处理。控制器使用了一个 model 为<br>Main  ：</p>
<pre><code>Ext.define(&apos;Calc.view.main.MainController&apos;, {
    extend: &apos;Ext.app.ViewController&apos;,
    alias: &apos;controller.main&apos;,   
    views: [&apos;Calc.view.main.Main&apos;],
    models: [&apos;Main&apos;],
    //这个 state 是一个自定义属性，用来跟踪计算器的状态。
        state: {
            operatorClicked: false,     
            selectedOperator: null,     
            dotClicked: false,
            op1: 0,     
            numberClicked: false,     
            sign: true,     
            decimal: false
        },
    onClickClear: function () {
        var vm = this.getViewModel();     
        vm.set(&apos;display&apos;,&apos;0&apos;);     
        this.state.selectedOperator=null;
        this.state.op1=0;     
        this.state.isPositive = true;     
        this.state.decimal = false;     
        this.state.sign = true;
    },
    onClickChangeSign: function (btn) {
        var vm = this.getViewModel();     
        var cur = vm.get(&apos;display&apos;);     
        if(cur!=&apos;0&apos;) {       
            if(this.state.sign===true ) {         
                vm.set(&apos;display&apos;, &apos;-&apos; + cur);
            }else {
                vm.set(&apos;display&apos;, cur.toString().substring(1));
            }
       }
       this.state.sign=!this.state.sign;
    },
    onClickOp: function (btn) {
        if(this.state.selectedOperator!=null &amp;&amp; this.state.numberClicked===true){
            var vm = this.getViewModel();       
            var op2 = parseFloat(vm.get(&apos;display&apos;));       
            var op1 = parseFloat(this.state.op1);
            var result = 0;
            switch(this.state.selectedOperator){         
                case &apos;+&apos;:         
                    result = op1 + op2;         
                    break;                 
                case &apos;-&apos;:         
                    result = op1 - op2;
                    break;                 
                case &apos;×&apos;:         
                    result = op1 * op2;         
                    break;                 
                case &apos;÷&apos;:         
                    result = op1 / op2;         
                    break;
            }
            vm.set(&apos;display&apos;, Math.round(result * 100) / 100);
            this.state.selectedOperator=null;
        }     
        if(btn.text!=&apos;=&apos;) {
            this.state.operatorClicked = true;
        }
        this.state.selectedOperator = btn.text;     
        this.state.numberClicked = false;
    },
    onClickDot: function (btn) {     
        if(this.state.decimal===false) {       
            var vm = this.getViewModel();       
            vm.set(&apos;display&apos;, vm.get(&apos;display&apos;) + &apos;.&apos;);
        }
    },
    onClickNumber: function (btn) {
        this.state.numberClicked = true;     
        if(this.state.selectedOperator ===&apos;=&apos;){
            this.onClickClear();
        }
        var vm = this.getViewModel();     
        if(this.state.operatorClicked===true) {       
            this.state.op1= vm.get(&apos;display&apos;);       
            vm.set(&apos;display&apos;, btn.text);       
            this.state.operatorClicked=false;
        }else{
            var cur = vm.get(&apos;display&apos;);       
            if(cur == &apos;0&apos;) {         
                cur = &apos;&apos;;
            }
            vm.set(&apos;display&apos;, cur + btn.text);
        }   
    } 
});
</code></pre><h2 id="视图模型-—-MainViewModel-js"><a href="#视图模型-—-MainViewModel-js" class="headerlink" title="视图模型 — MainViewModel.js"></a>视图模型 — MainViewModel.js</h2><p>这个  ViewModel  只有一个属性为  display<br>。这个用来绑定到计算器显示的值上。这里我们不会分别用一组字段创建模型，此外我们还将会硬编码数据。</p>
<pre><code>Ext.define(&apos;Calc.view.main.MainModel&apos;, {   
    extend: &apos;Ext.app.ViewModel&apos;,   
    alias: &apos;viewmodel.main&apos;,   
    data: {     
        display: 0.0
    }
});
</code></pre><p>在即将到来的章节中你将学习更多关于 模型，视图模型，字段，字段类型，校验 等等。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在本章中，你了解了不同的基本组件，例如 文本框，数字框，按钮，菜单等等。你已经学会如何使用表单字段设计一个表单和我们之前创建了一个简单的计算器项目。</p>
<p>在下一章中，你将学习关于数据包的内容，例如 数据源 ，模型 ，代理等等。store ，model ，这将是有益于处理数据的。</p>
<p><img src="https://img-blog.csdn.net/20170611153659679?watermark/2/text/aHR0cDovL2Jsb
2cuY3Nkbi5uZXQvbHN5MDkwMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/disso
lve/70/gravity/SouthEast" alt=""></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/Ext JS 6学习文档-第3章-基础组件/#comments" id="sourceId::2018/04/20/Ext JS 6学习文档-第3章-基础组件/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-Ext JS 6学习文档–第2章–核心概念" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/Ext JS 6学习文档–第2章–核心概念/">Ext JS 6学习文档–第2章–核心概念</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/Ext JS 6学习文档–第2章–核心概念/">
            <time datetime="2018-04-19T16:34:21.418Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ExtJs/">ExtJs</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/ext/">ext</a>, <a class="tag-link" href="/tags/extjs/">extjs</a>, <a class="tag-link" href="/tags/javascript/">javascript</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>#</p>
<h1 id="核心概念"><a href="#核心概念" class="headerlink" title="核心概念"></a>核心概念</h1><p>在下一章我们会构建一个示例项目，而在这之前，你需要学习一些在 Ext JS 中的核心概念，这有助于你更容易理解示例项目。这一章我们将学习以下知识点：</p>
<ul>
<li>类系统，创建和扩展类 </li>
<li>事件 </li>
<li>Ext JS 对象的查询 </li>
<li>容器 </li>
<li>布局   </li>
</ul>
<p>class system(类系统)</p>
<p>Ext JS 提供了很多功能，使得它创建和处理类变得简单。以下是在 Ext JS 6 的类系统中的几大组成类：</p>
<ul>
<li>Ext </li>
<li>Base </li>
<li>Class </li>
<li>ClassManager </li>
<li>Loader </li>
</ul>
<p>#####</p>
<h1 id="Ext-类"><a href="#Ext-类" class="headerlink" title="Ext 类"></a>Ext 类</h1><p>Ext  是一个全局单例的对象，在 Sencha library 中它封装了所有的类和许多实用的方法。许多常用的函数都定义在  Ext<br>对象里。它还提供了像其他类中一些频繁使用的方法的快速调用。</p>
<p>我们看一下在  Ext  类中定义的方法和属性：</p>
<h2 id="application-方法"><a href="#application-方法" class="headerlink" title="application 方法"></a>application 方法</h2><p>这里应用是用  Ext.application  方法初始化的。这个方法的参数是一个  Ext.app.Application  对象，这个方法会加载<br>Ext.app.Application  类，并在页面加载完成后开始应用给定的配置。</p>
<p>Ext.app.Application  这个类代表我们的整个应用，这在第1章(入门指南)讲过，讲过的吧？是吧？下面是<br>Ext.app.Application  的使用例子：</p>
<pre><code>Ext.application({   
    name: &apos;MyApp&apos;,   
    extend:&apos;MyApp.Application&apos;,   
    launch: function() {
    }
}) ;
</code></pre><p>上面代码创建一个名为  MyApp  的全局变量。我们的应用里所有的类都将归属于在这样一个命名空间下面。这将降低全局变量产生冲突的可能。</p>
<h2 id="define-方法"><a href="#define-方法" class="headerlink" title="define 方法"></a>define 方法</h2><p>你可以用这个方法定义或者重写一个类。 这个方法有三个参数，如以下代码所示。 在这里  name  参数是你要定义的类名，  data<br>参数是应用于这个类的属性，  callback  是可选参数，这个函数将会在这个类被创建后调用：</p>
<pre><code>Ext.define(name,data, callback)
</code></pre><p>下列代码创建一个名为  Car  的类：  </p>
<pre><code>Ext.define(&apos;Car&apos;, {   
    name: null,
    constructor: function(name) {     
        if (name) {       
            this.name = name;     
        }
    },
    start: function() {     
        alert(&apos;Car started&apos;);
    }
}) ;
</code></pre><p>你还可以使用  define  继承扩展一个类：  </p>
<pre><code>Ext.define(&apos;ElectricCar&apos;, {   
    extend: &apos;Car&apos;,   
    start: function() {
        alert(&quot;Electric car started&quot;);
    }
}) ;
</code></pre><p>如果你想替换一个父类方法的实现，你可以使用  Ext.define  来重写这个方法，如以下代码所示：</p>
<p>#</p>
<pre><code>Ext.define(&apos;My.ux.field.Text&apos;, {   
    override: &apos;Ext.form.field.Text&apos;,   
    setValue: function(val) {     
        this.callParent([&apos;In override&apos;]);     
        return this;
    }
});
</code></pre><p>细心的同学可能发现了当我们继承和重写时使用的属性是不同的，继承我们使用  extend  而重写使用  override<br>，这两者之间有什么区别呢？你一定感到疑惑，这里我非常有必要给你解释清楚，听我慢慢道来。</p>
<p>首先说继承并扩展一个类，这等同于是一个新的类，仍然可以在这个新的类里增加自己独有的方法和属性或者重写父类的方法。</p>
<pre><code>Ext.define(&apos;MyApp.view.main.Test&apos;, {
    extend: &apos;Ext.grid.Panel&apos;,
    xtype: &apos;maintest&apos;,
    title: &apos;Personnel&apos;,
    say:function(){
    alert(123);
    }
});
</code></pre><p>这里我继承了 gridpanel 类，并增加了一个 say方法，以下是输出调用 say 方法的运行结果。</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/1.png" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/1.png" alt="1">
</a></p>
<p>这应该很好理解，Test 继承了 grid panel 之后是一个新的类了，而这里如果创建 Ext.grid.Panel 对象是调用不了 say 方法的。</p>
<p>那么现在我把  extend  改为  override  我们再看一下：</p>
<pre><code>Ext.define(&apos;MyApp.view.main.Test&apos;, {
    override: &apos;Ext.grid.Panel&apos;,//改为 override 了
    xtype: &apos;maintest&apos;,
    title: &apos;Personnel&apos;,
    say:function(){
    alert(123);
    }
});


Ext.define(&apos;MyApp.Application&apos;, {
    extend: &apos;Ext.app.Application&apos;,

    name: &apos;MyApp&apos;,
    requires: [
        &apos;MyApp.view.main.Main&apos;,
    &apos;MyApp.view.main.Test&apos;//我这里引入了 Test 
    ],
    stores: [
        // TODO: add global / shared stores here
    ],

    launch: function () {
       var test = Ext.create(&quot;MyApp.view.main.Test&quot;,{
        title: &apos;Personnel&apos;
       });
       test.say();
    },

    onAppUpdate: function () {
        Ext.Msg.confirm(&apos;Application Update&apos;, &apos;This application has an update, reload?&apos;,
            function (choice) {
                if (choice === &apos;yes&apos;) {
                    window.location.reload();
                }
            }
        );
    }
});
</code></pre><p>运行结果：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/1-1.png" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/1-1.png" alt="1">
</a></p>
<p>我们可以看到我只是简单的把  extend  替换成  override  就报错说不能识别的类名。但是上面我也是引入了它的引用的(requires)，可见<br>extend 和 override 还是有区别的，我们不是重写(override)的是 grid panel 吗？那我们试试创建一个<br>Ext.grid.Panel  对象试试。</p>
<pre><code>Ext.define(&apos;MyApp.Application&apos;, {
    extend: &apos;Ext.app.Application&apos;,

    name: &apos;MyApp&apos;,
    requires: [
        &apos;MyApp.view.main.Main&apos;,
        &apos;MyApp.view.main.Test&apos;
    ],
    stores: [
        // TODO: add global / shared stores here
    ],

    launch: function () {
       //这里改成了 grid panel
       var test = Ext.create(&quot;Ext.grid.Panel&quot;,{
      title: &apos;Personnel&apos;
       });
       test.say();
    },

    onAppUpdate: function () {
        Ext.Msg.confirm(&apos;Application Update&apos;, &apos;This application has an update, reload?&apos;,
            function (choice) {
                if (choice === &apos;yes&apos;) {
                    window.location.reload();
                }
            }
        );
    }
});
</code></pre><p>再次运行结果：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/1-2.png" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/1-2.png" alt="1">
</a></p>
<p>这次正常了，我们使用 Ext.define 定义的类，使用了  override  重写组件，发现并不能新建我们定义的类，而是被重写的父类被新增了 say<br>方法。</p>
<p>所以总结一下，extend 会创建一个新的类，并继承父类的属性和方法，你也可以重写父类的方法。而 override<br>并不会创建一个新的类，而是修改这个被重写的父类。</p>
<ul>
<li>注意：例如上面的例子，Test 重写了 grid panel，我在其他类中创建 grid panel 时如果不引用 Test 那么重写仍然是不生效的。只有引用了 Test 重写才会生效 grid panel 才会具有 say 方法。不要问我为什么。 </li>
</ul>
<p>如果你想创建一个单例类，那么你在定义类时用 singleton 属性，如以下代码所示：</p>
<pre><code>Ext.define(&apos;Logger&apos;, {   
    singleton: true,   
    log: function(msg) {     
        console.log(msg);
    }
}) ;
</code></pre><h2 id="create-对象"><a href="#create-对象" class="headerlink" title="create 对象"></a>create 对象</h2><p>你可以使用下列代码来创建一个类的实例：</p>
<pre><code>Ext.create(Class,Options);
</code></pre><p>下列代码创建了一个  ElectricCar  类的实例，并传递一个值(name)：</p>
<pre><code>var myCar = Ext.create(&apos;ElectricCar&apos;,{     name: &apos;MyElectricCar&apos; }) ;
</code></pre><p>如果  Ext.Loader  是开启的，  Ext.create  执行时如果  ElectricCar  类不存在将会自动的下载对应的 JS 文件。默认<br>Ext.Loader  是开启的；你可以通过以下方式来关闭它。</p>
<pre><code>Ext.Loader.setConfig({  
   enabled: true
});
</code></pre><p>这里你也可以使用 new 关键字来创建一个实例，如以下代码所示；可是如果这个类不存在，使用 new 关键字创建实例并不会自动下载对应的 JS 文件：  </p>
<pre><code>var myCar = new ElectricCar(&apos;MyElectricCar&apos;);
</code></pre><ul>
<li>ps：你只需要掌握使用 Ext.create 创建实例就行了，new 关键字可能是兼容 Ext 3.x 的使用方式而继续支持的。new 关键字官方是不推荐用的。 </li>
</ul>
<h2 id="onReady"><a href="#onReady" class="headerlink" title="onReady"></a>onReady</h2><p>这个函数在页面加载完成后调用：</p>
<pre><code>Ext.onReady(function(){   
    new Ext.Component({     
        renderTo: document.body,     
        html: &apos;DOM ready!&apos;
    });
}) ;
</code></pre><p>大多时候，在你的代码里不会用到  onReady  这个方法，因为 Ext 建议你一个应用就是一个页面(单页式应用)，只有一个页面的话自然没有那么多场景会需<br>要用到。只有在极少数的一些特殊情况，你可能需要用它。这里要强调一点，如果你具有使用 jQuery 的基础，不要把  onReady  方法像 jQuery<br>中的  $( document ).ready()  那样频繁使用。</p>
<h2 id="widget-部件"><a href="#widget-部件" class="headerlink" title="widget (部件)"></a>widget (部件)</h2><p>当定义一个类时，你可以为这个类增加一个便于记忆的别名。例如：  Ext.panel.Panel  的别名为  widget.panel<br>。定义别名时，如以下代码所示指定  alias  属性：</p>
<p>Ext  .  define  (  ‘Ext.panel.Panel’  ,  {</p>
<p>extend  :  ‘Ext.container.Container’  ,</p>
<p>alias  :  ‘widget.panel’  //这里是定义的别名，</p>
<p>}  )  ;</p>
<p>你也可以使用 xtype 为这个类给定一个别名。这个 xtype 是非常有用的，当你以指定 xtype<br>的方式应用部件时，并不会创建实例，而是在真正调用展示的时候才会创建这个类的实例。在本章后面介绍 容器和布局 时你将会学到更多关于 xtype 的使用。</p>
<p>Ext.widget  方法是通过类的 xtype 快速创建部件的。</p>
<p>例如，不使用  widget  方法，你可以通过下列代码创建  Ext.panel.Panel  的实例：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {     
    renderTo: Ext.getBody(),     
    title: &apos;Panel&apos;   
});
</code></pre><p>反之，通过以下方式快速创建 panel 的实例：</p>
<pre><code>Ext.widget(&apos;panel&apos;, {       
    renderTo: Ext.getBody(),     
    title: &apos;Panel&apos;   
});
</code></pre><p>这里 panel 是一个容器，关于 panel 会在后面详细讲解。下面代码的作用相当于与上面：</p>
<pre><code>Ext.create(&apos;widget.panel&apos;, {     
    renderTo: Ext.getBody(),     
    title: &apos;Panel&apos;   
});
</code></pre><p>注意： 你阅读此文档的同时，这里面的大部分代码都是可以运行的，你可以选择在你本地设备上或者在 Sencha Fiddle 上执行这些示例代码。你可以访问<br>Sencha Fiddle  并将上面的代码键入到 launch 函数中，运行并查看结果。如果你访问了  <a href="https://fiddle.sencha.com/" target="_blank" rel="noopener"><br>https://fiddle.sencha.com </a> 将会看到下列代码：</p>
<pre><code>Ext.application({   
    name : &apos;Fiddle&apos;,   
    launch : function() {
        Ext.Msg.alert(&apos;Fiddle&apos;, &apos;Welcome to Sencha Fiddle!&apos;);
    }
}) ;
</code></pre><p>现在，粘贴创建 panel 部件的代码如以下示例，运行并查看结果。复制粘贴时，注意单引号不要写成中文标点单引号：</p>
<pre><code>Ext.application({   
    name : &apos;Fiddle&apos;,   
    launch : function() {
        Ext.create(&apos;widget.panel&apos;, {       
            renderTo: Ext.getBody(),       
            title: &apos;Panel&apos;
        });
    }
}) ;
</code></pre><ul>
<li>不是所有的代码都可以这样运行，此外并非所有的示例代码都会有视觉呈现。 </li>
</ul>
<h2 id="getClass"><a href="#getClass" class="headerlink" title="getClass"></a>getClass</h2><p>如果创建的实例是用 Ext.define 定义的，那么返回这个给定对象的类，否则返回 null：</p>
<pre><code>var button = new Ext.Button(); 
Ext.getClass(button); // returns Ext.Button
</code></pre><h2 id="getClassName"><a href="#getClassName" class="headerlink" title="getClassName"></a>getClassName</h2><p>通过它的引用或实例返回类名称：</p>
<pre><code>Ext.getClassName(Ext.Button); //returns &quot;Ext.Button&quot;
</code></pre><h2 id="Ext-Base"><a href="#Ext-Base" class="headerlink" title="Ext.Base"></a>Ext.Base</h2><p>这是所有  Ext  类的基础。所有的 Ext 类都继承自 Ext.Base。该类所有的原型和静态成员都会传递给继承它的类。</p>
<h2 id="Ext-Class"><a href="#Ext-Class" class="headerlink" title="Ext.Class"></a>Ext.Class</h2><p>这是一个低级别的工厂类，用于通过  Ext.ClassManager  定义一个类。所以不应该在你的代码中直接访问；你应该使用  Ext.define  。</p>
<h2 id="Ext-ClassManager"><a href="#Ext-ClassManager" class="headerlink" title="Ext.ClassManager"></a>Ext.ClassManager</h2><p>它管理所有的类同时处理类反射。通常通过下面几个方法访问：</p>
<ul>
<li>define </li>
<li>create </li>
<li>widget </li>
<li>getClass </li>
<li>getClassName </li>
</ul>
<p>在本章我们已经讨论使用过这些方法。</p>
<h2 id="Ext-Loader"><a href="#Ext-Loader" class="headerlink" title="Ext.Loader"></a>Ext.Loader</h2><p>用于动态的加载依赖。通常使用 Ext.require 来指定依赖。当你定义一个类时，这样指定组件的依赖列表是一个很好的做法，如以下代码所示：</p>
<pre><code>Ext.require([&apos;widget.window&apos;, &apos;layout.border&apos;,&apos;Ext.data.Connection&apos;]);
</code></pre><p>如果你需要引入一个指定命名空间下所有的 组件/类 时，使用通配符，如以下代码所示：  </p>
<pre><code>Ext.require([&apos;widget.*&apos;, &apos;layout.*&apos;, &apos;Ext.data.*&apos;);
</code></pre><p>使用以下语法排除掉不需要的类：</p>
<pre><code>Ext.exclude(&apos;Ext.data.*&apos;).require(&apos;*&apos;);
</code></pre><p>用这种方式，依赖的类是异步加载的。如果在你定义的类中没有指定依赖的类，那么当使用  Ext.Create<br>创建实例时，如果它是未加载的，这时将会同步加载这些类文件。这对性能有一定的影响，所以当你定义类时，使用  Ext.require  指定所需的类总是更好的。</p>
<ul>
<li>requires 属性加载需要的类时，当前类初始化之前被加载。 </li>
<li>uses 属性加载需要的类时，当前类初始化之后被加载。 </li>
<li><code>singleton:true 属性当前类初始化时,该实例是一个单例对象。</code></li>
</ul>
<p>注意：定位类的文件路径是基于类名的。例如：  MyApp.view.About  类的路径应该是  \myapp\view\ about.js  。</p>
<p>Events(事件)</p>
<p>一个事件可以是一个用户操作，一个 Ajax 调用的响应等等。</p>
<h2 id="Adding-listeners-为类添加监听"><a href="#Adding-listeners-为类添加监听" class="headerlink" title="Adding listeners(为类添加监听)"></a>Adding listeners(为类添加监听)</h2><p>当你创建对象或者创建以后都可以为这个对象添加监听器。下列示例代码为这个对象添加了一个 单击事件 的监听：</p>
<pre><code>Ext.create(&apos;Ext.Button&apos;, {   
    renderTo: Ext.getBody(),   
    listeners: {     
        click: function() {
            Ext.Msg.alert(&apos;Button clicked!&apos;);
        }
    }
}) ;
</code></pre><p>你可以添加多个事件监听，如以下代码示例：</p>
<pre><code>Ext.create(&apos;Ext.Button&apos;, {   
    renderTo: Ext.getBody(),   
    listeners: {     
        mouseout: function() {
            //Do something
        },     
        click: function() {       
            // Do something
        }   
    } 
});
</code></pre><p>你也可以在对象创建之后，使用  on  方法为对象添加事件监听：</p>
<pre><code>var button = Ext.create(&apos;Ext.Button&apos;);
button.on(&apos;click&apos;, function() {
   //Do something
}) ;
</code></pre><p>同样的，你也可以使用  on  方法一次添加多个事件的监听，如以下代码示例：  </p>
<pre><code>var button = Ext.create(&apos;Ext.Button&apos;);
button.on({
    mouseover: function() {
        //Do something
    },
    mouseover: function() {
        //Do something
    }
}) ;
</code></pre><h2 id="Removing-listeners-删除事件监听"><a href="#Removing-listeners-删除事件监听" class="headerlink" title="Removing listeners (删除事件监听)"></a>Removing listeners (删除事件监听)</h2><p>You can also remove the listeners, but you need the reference to the function;<br>you can’t use the anonymous function.</p>
<pre><code>var HandleClick= function() {
    Ext.Msg.alert(&apos;My button clicked!&apos;);
}

Ext.create(&apos;Ext.Button&apos;, {   
    listeners: {
       click: HandleClick
    } 
}) ; 

button.un(&apos;click&apos;, HandleClick);
</code></pre><h2 id="页面-DOM-元素的事件处理"><a href="#页面-DOM-元素的事件处理" class="headerlink" title="页面 DOM 元素的事件处理"></a>页面 DOM 元素的事件处理</h2><p>你可以将监听器添加到 DOM 元素，如下所示。</p>
<p>假设在你的 HTML 代码中，有一个  div  元素  id=mydiv  ，如以下代码所示：</p>
<pre><code>&lt; div id=&quot;mydiv&quot;&gt;&lt;/div &gt;
</code></pre><p>用下列代码为它添加事件监听：</p>
<pre><code>var div = Ext.get(&apos;mydiv&apos;);
div.on(&apos;click&apos;, function(e, t, eOpts) {
    // Do something 
});
</code></pre><p>访问 DOM</p>
<p>有三种方法来访问 DOM 元素：  get  ，  query  ，和  select  。</p>
<h2 id="Ext-get"><a href="#Ext-get" class="headerlink" title="Ext.get"></a>Ext.get</h2><p>get 方法是根据这个 DOM 元素的 ID 检索获取并封装为  Ext.dom.Element  对象：</p>
<pre><code>var mydiv = Ext.get(&apos;myDivId&apos;);
</code></pre><h2 id="Ext-query"><a href="#Ext-query" class="headerlink" title="Ext.query"></a>Ext.query</h2><p>这种方式基于传入的 CSS 选择器 从给定的根节点开始查找。它返回一个匹配选择器的元素(  HTMLElement[]/Ext.dom.Element[]<br>)数组。如果没有匹配的，返回一个空值的数组对象。</p>
<p>在下面示例中，  myCustomComponent.getEl().dom  是传递的根节点。  Ext.query<br>将检索这个节点内的子元素，并返回一个数组包含 CSS class 为 ‘  oddRow  ‘ 的的元素：</p>
<pre><code>var someNodes = Ext.query(&apos;.oddRow&apos;, myCustomComponent.getEl().dom);
</code></pre><h2 id="Ext-select"><a href="#Ext-select" class="headerlink" title="Ext.select"></a>Ext.select</h2><p>给出一些 CSS/XPath 选择器，Ext.select 方法返回一个  CompositeElement  类型的对象，代表一个元素的集合。</p>
<p>这个  CompositeElement  对象可以进行过滤，迭代，和对整个集合执行整体操作等等：</p>
<pre><code>var rows = Ext.select(&apos;div.row&apos;); ////Matches all divs with class
row rows.setWidth(100); // 这是设置所有元素的宽度为 100
</code></pre><p>你也可以用一行代码，如下所示：</p>
<pre><code>Ext.select(&apos;div.row&apos;).setWidth(100);
</code></pre><h2 id="多重选择器"><a href="#多重选择器" class="headerlink" title="多重选择器"></a>多重选择器</h2><p>在方法调用时通过指定多个搜索条件可以用来匹配多个元素：</p>
<pre><code>Ext.select(&apos;div.row, span.title&apos;); //匹配所有的 class 用 .row 的 div 元素，和匹配所有 class 用 .title 的 span 元素
</code></pre><h2 id="选择器-根"><a href="#选择器-根" class="headerlink" title="选择器 根"></a>选择器 根</h2><p>当你使用 select ，它默认取 HTML body 作为根并从默认的 body 开始检索整个 DOM<br>树。你可以通过制定一个根元素来避免这种情况，这样它将只搜索给定的根的子节点。</p>
<pre><code>Ext.get(&apos;myEl&apos;).select(&apos;div.row&apos;);
</code></pre><p>这儿使用了 ‘myEl’ 作为根节点。这将首先找到 id 为 ‘myEl’ 的元素，然后将在根元素(myEl)下面搜索出 class 为 ‘row’ 的<br>div 标签。</p>
<pre><code>Ext.select(&apos;div.row&apos;, true, &apos;myEl&apos;);// This is equivalent to the previous line.
</code></pre><h2 id="链式选择器"><a href="#链式选择器" class="headerlink" title="链式选择器"></a>链式选择器</h2><p>下列的查询方式会匹配 class 为 row 并且 title 属性值为 bar 的 div ，这个 div 属于其父元素的首个子元素：</p>
<pre><code>Ext.select(&apos;div.row[title=bar]:first&apos;)
</code></pre><h2 id="Ext-ComponentQuery"><a href="#Ext-ComponentQuery" class="headerlink" title="Ext.ComponentQuery"></a>Ext.ComponentQuery</h2><p>这允许你用 ID，xtype，和 属性查找一个组件。你可以全局搜索或者指定一个根组件。</p>
<p>下列查询将返回所有的 xtype 为  button  的组件：</p>
<pre><code>Ext.ComponentQuery.query(&apos;button&apos;);
</code></pre><p>得到一个 id 为  foo  的组件，用以下代码：</p>
<pre><code>Ext.ComponentQuery.query(&apos;#foo&apos;);
</code></pre><p>下列代码将返回所有的 xtype 为 button 并且 title 属性值为 my button 的组件：</p>
<pre><code>Ext.ComponentQuery.query(&quot;button[title=&apos;my button&apos;]&quot;);; //or parent.query(&apos;textfield[title=my button]&apos;);
</code></pre><p>你也可以使用嵌套选择器如下：You can also use nested selectors as follows:</p>
<pre><code>Ext.ComponentQuery.query(&apos;formpanel numberfield&apos;); // 这里获取 xtype 为 frompanel 下面的 xtype　为 numberfield 的组件
</code></pre><p>下列代码返回这个 parent 容器内匹配传递进来的选择器的第一个直接子组件，如果没有匹配上，返回 null 。</p>
<pre><code>parent.child(&apos;button[itemId=save]&apos;);
</code></pre><p>同样的，你也可以使用其他的方法，例如  nextNode  ,  up  ,  down  ,  previousSibling  等等。</p>
<h2 id="组件，容器，和布局"><a href="#组件，容器，和布局" class="headerlink" title="组件，容器，和布局"></a>组件，容器，和布局</h2><p>Ext JS 提供了一组丰富的组件和布局，这使在 Ext JS 中开发 UI 变得超级简单，甚至非专业 UI 开发人员也能够轻易的使用。</p>
<h2 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h2><p>从简单的组件说起，例如 button 和 label ，到复杂的组件，例如 Tree Panel，Grids 等等，Ext JS<br>有大量的内置组件。所有的组件都派生自  Ext.Component  类，它提供支持创建，重绘，渲染和处理组件。</p>
<p>所有的组件都有一个属性叫做  xtype<br>。它是非常有用的，它用在当你不想马上实例化这个组件时，而是想让这个组件在实际被应用时才创建，就是我们俗称的懒加载。</p>
<p>容器</p>
<p>容器是一个特殊的组件类型，它能够持有其他组件。在 Ext JS 中  Ext.container.Container  类是所有的容器的基础类。</p>
<p>Ext.toolbar.Toolbar  ,  Ext.panel.Panel  ,  和  Ext.Editor<br>是一些内置组件。这些组件都是可以包含其他组件。而像  Ext.button.Button  类就不是派生自  Ext.container.Container<br>，所以它不能够包含其他组件。</p>
<p>一个典型的 Ext JS 应用包含一组嵌套的组件。看下面这个例子并思考：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo:Ext.getBody(),   
    width:700,
    height:400,
    items:[{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 1&apos;,
    },{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 2&apos;,     
        height: 200,     
        items: [{
            xtype: &apos;button&apos;,       
            text: &apos;Click Me&apos;
        }]
    },{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 3&apos;,     
        width: 150,     
        height: 100
    }]
});
</code></pre><p>在前面的代码中，这是嵌套的组件，结构如下图所示：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/112.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/112.jpg" alt="1">
</a></p>
<p>上面的代码运行后将输出类似以下截图：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/113.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/113.jpg" alt="1">
</a></p>
<h2 id="布局"><a href="#布局" class="headerlink" title="布局"></a>布局</h2><p>布局定义了包含的组件是如何定位的以及设定组件的尺寸大小。每一个容器都有一个布局。默认布局是 auto 。这将不会为子组件指定任何关于位置和大小的规则。</p>
<p>在上面的图中，你可能已经注意到这些子组件只是一个接一个嵌套在父级容器中。这是在代码中因为我们还没有为这些组件制定任何布局，默认情况下使用的是 auto<br>布局。</p>
<p>现在，让我们在相同的代码里使用一些布局。下列示例中，我们将使用 column 布局和 center 布局。</p>
<p>当你使用 column 布局，你可以指定  columnWidth  。所有的列的  columnWidth  的值的总和必须等于  1<br>。你也可以为一些列指定固定宽度，如以下代码所示。这里，Panel3 取了一个 150 的固定宽度，然后剩下的两列按照  columnWidth<br>的值分配剩下的宽度：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo:Ext.getBody(),   
    width:700,   
    height:400,   
    layout:&apos;column&apos;,   
    items: [{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 1&apos;,     
        columnWidth: 0.4,     
        height: 400,
    },{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 2&apos;,     
        columnWidth: 0.6,     
        layout: &apos;center&apos;,     
        height: 400,     
        items: [{
            xtype: &apos;button&apos;,       
            text: &apos;Click Me&apos;
        }]
    },{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 3&apos;,     
        width: 150,     
        height: 400
    }]
});
</code></pre><p>以上代码输出为：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/114.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/114.jpg" alt="1">
</a></p>
<h2 id="updateLayout"><a href="#updateLayout" class="headerlink" title="updateLayout"></a>updateLayout</h2><p>updateLayout  是  Ext.container.Container<br>对象里的一个方法。这可以用来根据布局规则重新定位子组件。例如你修改了布局方式，需要动态的更新布局时。</p>
<h2 id="suspendLayout"><a href="#suspendLayout" class="headerlink" title="suspendLayout"></a>suspendLayout</h2><p>大多数时候你不会用到这个  updateLayout  方法，然而有些时候你必须调用它。</p>
<p>这个  updateLayout  方法是在你重绘和当你添加或删除了一个组件时自动调用。有时候你可能需要它暂停一下，不是马上就调用，特别是当你添加或删除多个<br>子组件时。所以在这种情况下，你可以设置  suspendLayout  属性为 true ，一旦你完成添加或删除组件的操作，你可以设置<br>suspendLayout  为 false 并在你的代码中手动调用  updateLayout  方法。</p>
<p>同样的如果你想对整个框架停止更新布局，你可以调用  Ext.suspendLayouts()  ，然后在你的操作完成后你可以通过调用<br>Ext.resumeLayouts(true)  恢复它。</p>
<p>以下是 Ext JS 中可用的布局：</p>
<ul>
<li>absolute </li>
<li>accordion </li>
<li>anchor </li>
<li>border </li>
<li>card </li>
<li><p>center </p>
</li>
<li><p>column </p>
</li>
<li>fit </li>
<li>hbox </li>
<li>table </li>
<li>vbox </li>
</ul>
<h2 id="absolute-绝对布局"><a href="#absolute-绝对布局" class="headerlink" title="absolute 绝对布局"></a>absolute 绝对布局</h2><p>这个布局使用 x 和 y 属性来指定组件的绝对定位：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo:Ext.getBody(),   
    width:700,   
    height:400,   
    layout:&apos;absolute&apos;,   
    items: [{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 1&apos;,     
        x: 12,     
        y: 20,     
        height: 250
    },{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 2&apos;,     
        x: 200,     
        y: 150,     
        height: 200
    },{     
        xtype: &apos;panel&apos;,     
        title: &apos;Panel 3&apos;,     
        x: 400,     
        y: 250,     
        width: 150,     
        height: 100
    }]
});
</code></pre><p>这里所示的输出，你可以重叠组件因为他们用绝对位置定位的：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/115.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/115.jpg" alt="1">
</a></p>
<h2 id="accordion-手风琴-可折叠-布局"><a href="#accordion-手风琴-可折叠-布局" class="headerlink" title="accordion 手风琴(可折叠)布局"></a>accordion 手风琴(可折叠)布局</h2><p>这个布局展示了在一个时间里只有一个内置的可支持折叠和展开的子级 panel 。瞧一下以下示例：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo: Ext.getBody(),   
    width: 700,   
    height: 400,   
    layout: &apos;accordion&apos;,   
    items: [{
        title: &apos;Item 1&apos;,
        html: &apos;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum&apos;
    },{
        title: &apos;Item 2&apos;,     
        html: &apos;some content here&apos;
    },{
        title: &apos;Item 3&apos;,     
        html: &apos;empty&apos;
    }] 
});
</code></pre><p>这里显示的输出，这个  Item 1  是展开的，而其他的 panel 是折叠的：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/116.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/116.jpg" alt="1">
</a></p>
<h2 id="anchor-锚点布局"><a href="#anchor-锚点布局" class="headerlink" title="anchor 锚点布局"></a>anchor 锚点布局</h2><p>这个布局使你能够指定子级组件的大小，而这是相对于布局容器的。首先容器根据指定的锚点规则调整然后所有的子级组件再作调整：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo: Ext.getBody(),   
    width: 700,
    height: 400,   
    layout: &apos;anchor&apos;,   
    items: [{     
        title: &apos;Item 1&apos;,     
        html: &apos;Item 1&apos;,     
        anchor: &apos;50%&apos;
    },{     
        title: &apos;Item 2&apos;,     
        html: &apos;Item 2&apos;,     
        anchor: &apos;-20 -200&apos;
    },{     
        title: &apos;Item 3&apos;,     
        html: &apos;Item 3&apos;,     
        anchor: &apos;-200&apos;
    }]
});
</code></pre><p>输入如以下截图：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/117.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/117.jpg" alt="1">
</a></p>
<p>####</p>
<h2 id="border-布局"><a href="#border-布局" class="headerlink" title="border 布局"></a>border 布局</h2><p>这个布局允许你为子组件指定一个区域位置，例如 center，north，south，west 和 east。当你使用 border<br>布局时，在其内的组件必须有一个指定区域为 center，如下列代码所示：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo: Ext.getBody(),   
    width: 700,   
    height: 400,   
    layout: &apos;border&apos;,
    items: [{     
        title: &apos;Item 1&apos;,     
        html: &apos;Item 1&apos;,     
        region: &apos;center&apos;
    },{     
        title: &apos;Item 2&apos;,     
        html: &apos;Item 2&apos;,     
        region: &apos;east&apos;,     
        width: 200
    },{     
        title: &apos;Item 3&apos;,     
        html: &apos;Item 3&apos;,     
        region: &apos;south&apos;,     
        height: 100
    }] 
}) ;
</code></pre><p>以上代码输出类似下列视图：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/118.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/118.jpg" alt="1">
</a></p>
<h2 id="card-卡片布局"><a href="#card-卡片布局" class="headerlink" title="card 卡片布局"></a>card 卡片布局</h2><p>在此布局中，只有一个子组件是可见的，这个组件基本上充满整个容器。卡片布局一般应用在向导或者 tabs：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo: Ext.getBody(),   
    width: 700,   
    height: 400,   
    layout: &apos;card&apos;,   
    defaultListenerScope: true,   
    bbar: [&apos;-&gt;&apos;,{
        itemId: &apos;btn-prev&apos;,     
        text: &apos;Previous&apos;,     
        handler: &apos;showPrevious&apos;,     
        disabled: true
    },{
        itemId: &apos;btn-next&apos;,     
        text: &apos;Next&apos;,     
        handler: &apos;showNext&apos;
    }],   
    items: [{     
        index: 0,     
        title: &apos;Item 1&apos;,     
        html: &apos;Item 1&apos;
    },{     
        index: 1,     
        title: &apos;Item 2&apos;,     
        html: &apos;Item 2&apos;
    },{     
        index:2,     
        title: &apos;Item 3&apos;,     
        html: &apos;Item 3&apos;
    }],
    showNext: function () {     
        this.navigate(1);
    },
    showPrevious: function () {     
        this.navigate(-1);
    },
    navigate: function (incr) {     
        var layout = this.getLayout();     
        var index = layout.activeItem.index + incr;     
        layout.setActiveItem(index);
        this.down(&apos;#btn-prev&apos;).setDisabled(index===0);     
        this.down(&apos;#btn-next&apos;).setDisabled(index===2);
    }
});
</code></pre><p>卡片布局的输出。当你点击 next 按钮，将会显示 Item 2 面板：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/119.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/119.jpg" alt="1">
</a></p>
<h2 id="center-中心布局"><a href="#center-中心布局" class="headerlink" title="center 中心布局"></a>center 中心布局</h2><p>这种布局，容器的子组件在中间。在本章中开始介绍布局的部分，我们已经有一个例子了。</p>
<h2 id="column-列布局"><a href="#column-列布局" class="headerlink" title="column 列布局"></a>column 列布局</h2><p>用此布局，你可以将容器划分为指定数量的列并指定每列所占的大小。这个例子也在本章开始介绍布局的部分中可以找到。</p>
<h2 id="fit-适配布局"><a href="#fit-适配布局" class="headerlink" title="fit 适配布局"></a>fit 适配布局</h2><p>在此布局中，子组件将会自适应容器的尺寸。如下：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo: Ext.getBody(),   
    width: 700,
    height: 400,   
    layout: &apos;fit&apos;,   
    bodyPadding: 20,   
    items: [{
        title: &apos;Item 1&apos;,
        html: &apos;Fills the container&apos;,
    }]
});
</code></pre><p>下列截图展示以上代码的输出。注意：Item 1 组件与父级容器之间的空隙是我们指定了  bodyPadding  属性：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/120.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/120.jpg" alt="1">
</a></p>
<h2 id="hbox-布局"><a href="#hbox-布局" class="headerlink" title="hbox 布局"></a>hbox 布局</h2><p>这种布局与 column 布局几乎是一样的，但是这种布局允许你拉伸列的高度。这里使用 flex 选项为子组件设置水平的相对值：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo: Ext.getBody(),   
    width: 700,   
    height: 400,   
    layout:{     
        type: &apos;hbox&apos;,     
        pack: &apos;start&apos;,     
        align: &apos;stretch&apos;,
    },
    items: [{
        title: &apos;Item 1&apos;,
        html: &apos;Item 1&apos;,    
        flex: 1
    },{
        title: &apos;Item 2&apos;,     
        html: &apos;Item 2&apos;,     
        width: 100
    },{
        title: &apos;Item 3&apos;,     
        html: &apos;Item 3&apos;,     
        flex: 2
    }] 
});
</code></pre><p>上面代码输出：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/121.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/121.jpg" alt="1">
</a></p>
<h2 id="table-表格布局"><a href="#table-表格布局" class="headerlink" title="table 表格布局"></a>table 表格布局</h2><p>这个布局允许你渲染一个表格出来。你可以指定列数和行数，使用  rowspan  和  colspan  创建复杂布局：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo: Ext.getBody(),   
    width: 700,
    height: 400,   
    layout:{     
        type: &apos;table&apos;,     
        columns: 3,     
        tableAttrs: {       
            style: {         
                width: &apos;100%&apos;
            }
        }
    },   
    items: [{     
        rowspan: 3,     
        title: &apos;Item 1&apos;,     
        html: &apos;Item 1&apos;
    },{     
        title: &apos;Item 2&apos;,     
        html: &apos;Item 2&apos;
    },{     
        title: &apos;Item 3&apos;,     
        rowspan: 2,     
        html: &apos;Item 3&apos;
    },{     
        title: &apos;Item 4&apos;,     
        html: &apos;Item 4&apos;
    },{     
        title: &apos;Item 5&apos;,     
        html: &apos;Item 5&apos;
    },{     
        title: &apos;Item 6&apos;,     
        html: &apos;Item 6&apos;
    },{     
        title: &apos;Item 7&apos;,     
        html: &apos;Item 7&apos;
    }]
});
</code></pre><p>以下截图所示为前面 table 布局代码的输出结果：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/122.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/122.jpg" alt="1">
</a></p>
<h2 id="VBox-布局"><a href="#VBox-布局" class="headerlink" title="VBox 布局"></a>VBox 布局</h2><p>这个布局内，子组件是垂直向下一个接一个排列。看一下以下的示例代码：</p>
<pre><code>Ext.create(&apos;Ext.panel.Panel&apos;, {   
    renderTo: Ext.getBody(),   
    width: 700,   
    height: 400,   
    layout:{     
        type: &apos;vbox&apos;,     
        pack: &apos;start&apos;,     
        align: &apos;stretch&apos;,
    },   
    items: [{
        title: &apos;Item 1&apos;,     
        html: &apos;Item 1&apos;,     
        flex: 1
    },{
        title: &apos;Item 2&apos;,     
        html: &apos;Item 2&apos;,     
        height: 100
    },{
        title: &apos;Item 3&apos;,     
        html: &apos;Item 3&apos;,
        flex: 2
    }]
});
</code></pre><p>这段代码所示的输出：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/123.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/123.jpg" alt="1">
</a></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在本章中，我们了解学习了在 Ext JS 中的基础类和这些类中常用的方法，你还学习了如何创建和扩展一个类。还有如何使用 事件 和 查询元素及组件的功能。</p>
<p>在下一章，我们将会看一看许多有用的组件，例如 buttons ，menus ，toolbars 等等。我们也将根据所学创建一个小型的计算器应用。</p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/Ext JS 6学习文档–第2章–核心概念/#comments" id="sourceId::2018/04/20/Ext JS 6学习文档–第2章–核心概念/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-Ext JS 6学习文档–第1章–ExtJS入门指南" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/Ext JS 6学习文档–第1章–ExtJS入门指南/">Ext JS 6学习文档–第1章–ExtJS入门指南</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/Ext JS 6学习文档–第1章–ExtJS入门指南/">
            <time datetime="2018-04-19T16:34:21.417Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/ExtJs/">ExtJs</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/ext/">ext</a>, <a class="tag-link" href="/tags/extjs/">extjs</a>, <a class="tag-link" href="/tags/javascript/">javascript</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>##</p>
<p>Ext JS 入门指南</p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本来我是打算自己写一个系列的 ExtJS 6 学习笔记的，因为 ExtJS 6 目前的中文学习资料还很少。google 搜索资料时找到了一本国外牛人写的关于<br>ExtJS 6 的电子书 [Ext JS 6 By Example]。这份资料在 <a href="https://www.packtpub.com/all/?search=ext%20js%206" target="_blank" rel="noopener"> PACKT
</a> 上卖 35.99 刀的，当然了万能的 google<br>还是帮我下载到了 PDF<br>文档。大概看了一下，讲的很详细，例子也比较简单，容易理解，现我准备利用工作之余翻译这份文档，为自己学习加深理解，也希望能帮助更多的人学习。</p>
<p>翻译时，我并不会 100%<br>按照原文照搬，而是可能加上我的理解和我自己的看法，所谓求其上而得其中，求其中而得其下，如果你看到这个文章，也不要以此作为标准，保持你的意见和思想。</p>
<h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><p>现在如果你打算学习 Ext JS 了，那你应该告别使用普通的 JavaScript 了。JavaScript 是一个伟大，卓越的语言，但是随着 web<br>应用越来越大，我们的代码也越来越难以维护，这是十分困难并耗费时间的。</p>
<p>普通的 JavaScript 非常的灵活，这有很多优点，同时这样也会带来很多问题，随着你的应用越来越大，参与的人就越来越多，普通的 JavaScript 让<br>10 个人来写，就会有 10 种不同的风格，往往有些代码只有写代码的当事人知道为什么这么写，如果这个人不再负责这个功能，就会成为后来人的坑。所以在代码上的约<br>束和规范是十分有必要的。这也是为什么我一直喜欢 ExtJS 的原因，ExtJS 写起来代码就像 Java 一样，非常严谨，非常面向对象。缺点就是在前端 JS<br>库中属于比较重量级，学习曲线相对陡峭。</p>
<p>当涉及到 JavaScript 框架时，有客户端的 JavaScript 框架以及服务端的 JavaScript 框架。Ext JS 就是一个客户端的<br>JavaScript 框架。</p>
<p>在本章基本上是讲述关于如何使用我们所需的工具来设置开发环境以及Ext JS 的基本介绍。在这一章，我们主要涉及以下几点  ：</p>
<ul>
<li>使用 Ext JS 的优点 </li>
<li>对 Ext JS 的介绍 </li>
<li>安装设置 Sencha Cmd 和 Ext JS </li>
<li>使用 SenchaCmd 搭建 Ext JS 应用 </li>
<li>Ext JS 应用的体系结构 </li>
<li>探索 Sencha Cmd 命令 </li>
<li>如何调试 Ext JS 应用 </li>
<li>使用开发工具 IDE </li>
</ul>
<h3 id="为什么使用-Ext-JS"><a href="#为什么使用-Ext-JS" class="headerlink" title="为什么使用 Ext JS?"></a>为什么使用 Ext JS?</h3><p>既然推荐你使用，自然要告诉你使用 Ext JS 都有哪些看得见的好处。现在让我们瞧一瞧，在你的应用里使用 Ext JS 相较普通 JavaScript<br>有哪些优点。</p>
<h4 id="跨浏览器支持"><a href="#跨浏览器支持" class="headerlink" title="跨浏览器支持"></a>跨浏览器支持</h4><p>只要你做 web 开发，你一定解决过浏览器兼容问题，这有多么消耗时间和影响工作进度你一定有体会，你可能会花上几小时甚至几天来解决跨浏览器<br>bug。你为什么不把这些时间用来关注你的业务功能呢？相反，如果使用 Ext JS 这个 JavaScript<br>框架，这种事情将由它去考虑，而你可以专注于业务功能。</p>
<h4 id="丰富的-UI-组件"><a href="#丰富的-UI-组件" class="headerlink" title="丰富的 UI 组件"></a>丰富的 UI 组件</h4><p>Ext JS 提供了大量丰富的 UI 控件，如 data grid ，tab panels,tree 控件，日期选择工具，图表 等等，Ext JS<br>作为一站式的前端框架，有很多优秀且实用的控件，尤其是表格对复杂数据展示的支持是其他的 JavaScript 框架不具备的， 这些都能大量节省你的开发时间。</p>
<h4 id="双向数据绑定"><a href="#双向数据绑定" class="headerlink" title="双向数据绑定"></a>双向数据绑定</h4><p>双向数据绑定意味着当视图 view 的数据发生变化，你的模型 model 将自动更新。同样的，当你的应用更新这个模型 model，这个数据也将自动传递到<br>view。</p>
<p>就拿编辑页面来举栗。 当这个页面(view)加载，这时已经渲染了来自模型(model) 的数据到<br>HTML，并且当用户在页面(view)上更新这个数据，这时候你需要更新你的模型(model)，但如果你使用 Ext JS 将不需要你自己去做这些编程。</p>
<h4 id="JavaScript-的架构模式-MVC-MVVM"><a href="#JavaScript-的架构模式-MVC-MVVM" class="headerlink" title="JavaScript 的架构模式(MVC/MVVM)"></a>JavaScript 的架构模式(MVC/MVVM)</h4><p>随着越来越多的代码转移到客户端，维护客户端的 JavaScript 代码变的非常困难。通过在客户端使用  MVC  (  Model View<br>Controller  )/  MVVM  (  Model View, View Model  )  使维护客户端的 JavaScript<br>变得更容易，并提高了生产效率。关于 MVC 和 MVVM 架构模式会在本章后面解释。</p>
<h4 id="简化复杂的操作"><a href="#简化复杂的操作" class="headerlink" title="简化复杂的操作"></a>简化复杂的操作</h4><p>想象一下使用纯 JavaScript 写一个 AJAX 调用，你需要考虑确保支持所有浏览器，这你可以看一下任何的 JavaScript 框架里进行 AJAX<br>调用的方法的源码。想象一下使用纯 JavaScript 创建一个复杂的控件，例如 grid ，分页，排序，过滤，分组，可编辑字段等等。Ext JS<br>不需要你自己来做这些工作。这里其实是废话，没有谁闲的蛋疼重复造轮子。但是其他框架一定没有 Ext JS 这种一站式的支持。</p>
<h4 id="访问-DOM-变得容易"><a href="#访问-DOM-变得容易" class="headerlink" title="访问 DOM 变得容易"></a>访问 DOM 变得容易</h4><p>普通的纯 JavaScript 里，你可以访问 DOM，但是这有点复杂。使用 Ext JS 就会很容易查找 DOM。当然使用 jquery<br>的人肯定不信服，还能比 jquery 查找 DOM 更方便吗？我也常用 jquery ，这里我可以说 Ext JS 相较 jquery 查找 DOM<br>不逞多让。</p>
<h4 id="客户端路由"><a href="#客户端路由" class="headerlink" title="客户端路由"></a>客户端路由</h4><p>在 web 应用里，URL 映射到 web 页面的路由方式背后有一系列的逻辑。路由可以在服务端也可以在客户端。通常客户端路由都是用单页式应用实现的。<br>Single-Page Application  (  SPA  )。</p>
<h4 id="支持无障碍访问"><a href="#支持无障碍访问" class="headerlink" title="支持无障碍访问"></a>支持无障碍访问</h4><p>无障碍访问特性意味着这个应用程序的内容必须是可以方便的让视力受损的人借助辅助设备访问(例如屏幕阅读器)。<br>而开发一个应用程序能够很好的支持无障碍特性是非常困难的。这里 Ext JS 已经为你做了。</p>
<p>在美国，如果你开发的软件供联邦和州政府职员使用，在绝大多数情况下，你需要确保你的程序是支持无障碍访问特性的。这一点，很少 JavaScript<br>框架能对无障碍访问特性有较好的支持。而 Ext JS 提供了完美的无障碍访问支持。</p>
<p>World Wide Web Consortium  (  W3C  )已经创立了一个名为  WAI-ARIA  (  Web Accessibility<br>Initiative – Accessible Rich Internet Applications  ) 的技术规范。</p>
<ul>
<li>WAI-ARIA, 是Web Accessibility Initiative – Accessible Rich Internet Applications 的缩写，指无障碍网页应用技术。她主要解决的一个问题:让残障人士能无障碍地访问网页上的动态内容。 </li>
</ul>
<p>这种规范方式使残疾人也能无障碍访问 web 应用。 Ext JS 对此具有良好的支持，并且所有的 控件/部件 都可以支持，不需要你做任何额外的编码。</p>
<p>###</p>
<h3 id="介绍-Ext-JS"><a href="#介绍-Ext-JS" class="headerlink" title="介绍 Ext JS"></a>介绍 Ext JS</h3><p>Ext JS 作为一个一站式的开发富 UI 应用的框架，它提供支持 MVC,MVVM, 双向绑定，跨浏览器兼容，路由功能，一组广泛的丰富的 UI<br>组件，图表等等。对于框架中所有的 API ， Ext JS 还拥有一个非常优秀的文档。Ext JS 最初的建立是由 Jack Slocum 开发的，做为<br>YUI 的一个附加扩展库使用，现在则是成为 Sencha 公司的产品。</p>
<p>在 Ext JS 中，你写的代码基本上就是 JavaScript，因为你不需要写 HTML。Ext JS 附带一组庞大的丰富的 UI<br>组件，这在你的开发过程中会节约你相当多的时间。</p>
<p>本书中，所有的样例代码和示例项目代码将使用最新的 Ext JS Version 6，但是大多数代码是兼容以前的 Ext JS 5 的。Ext JS 5 和<br>Ext JS 6 的大多数概念是相同的。所以如果你在使用 Ext JS 5，你仍然可以从这本书中受益不少。但是请记住这本书的代码将不会运行在 Ext JS<br>5 并且可能需要做一些小修改才能运行在 Ext JS 5。在 Ext JS 6 中最重要的变化是它合并了两个框架：Ext JS 和 Sencha Touch<br>合并成为了一个框架。Ext JS 6 还带来了一个新的 SASS 编译器叫 Fashion，以及3D 图表的改进等等。</p>
<p>要明白为什么会发生合并 Ext JS 和 Sencha Touch , 我们需要回顾一下。</p>
<p>Sencha Touch 是一个独立的产品，专用于为移动设备和平板电脑创建可支持触摸的应用，它利用硬件加速技术为移动设备提供高性能的 UI 组件。</p>
<p>Ext JS 4 和 Ext JS 5 主要用于开发桌面级的 web 应用。 如果你已经在 Ext JS 5 或 Ext JS 4 创建了用于桌面的 web<br>应用，这仍然可以在移动设备和平板电脑上运行，但是它将不支持一些特定的触摸功能，并且不能利用硬件加速为移动设备提供高性能 UI<br>组件。所以为了更好的支持移动设备，Sencha 开发人员被告知要使用 Sencha Touch。</p>
<p>使用 Sencha Touch 有很多优势。 在 Sencha Touch<br>编写的应用将具备移动平台本地应用一样的外观，并且性能将更好。然而许多开发人员有一个抱怨,因为他们被迫保持两套相同的应用程序的代码库。</p>
<p>尽管 Sencha Touch 和 Ext JS 有很多差异是完全不同的产品，但他们的概念和思想的框架非常相似，如果你会用 Ext JS,那么学习<br>Sencha Touch 也非常容易。</p>
<p>长期以来,许多 Ext JS 和 Sencha Touch 用户问为什么不把两个产品合二为一使 Ext JS 能够带触摸功能。在 Ext JS 6<br>中,Sencha 决定将这两个产品合并到一个单一的产品。</p>
<p>现在，在 Ext JS 6 你仍然可以维护一套独立的代码。而要 Sencha Touch 和 Ext JS 6<br>两者兼存，有些视图的代码你可能需要单独的编写，但是大部分代码都是共享的。</p>
<p>在 Ext JS 6 合并后将两者通用的代码做为核心(两者公用一个核心)，并且引入了一个 toolkit(工具箱) 的概念。toolkit<br>是一个可视化组件的包，例如 button，panel 等等。Ext JS 6 有两个工具包：classic(古典) 和 modern(现代)。原来的 Ext<br>JS 的可视化组件放在 classic 工具包，原来的 Sencha Touch 的可视化组件则放在 modern 工具包。</p>
<p>在 Ext JS 6 中选择你想使用的 toolkit(工具包)很简单，如果你正在开发的程序只是针对移动设备的，你可以选择<br>modern，如果你只用于桌面那么你可以选择 classic。默认则是通用的，通用的应用你在桌面端访问 Ext JS 6 应用，就会自动展现 classic<br>风格，如果用移动设备或平板电脑访问则自动展现 modern 风格。</p>
<h3 id="通用应用"><a href="#通用应用" class="headerlink" title="通用应用"></a>通用应用</h3><p>如果你的应用要同时支持桌面和移动设备，在 Ext JS 6<br>你可以创建一个通用的应用，这时候将会同时应用两个工具包。你可以添加以下的构建配置(这个配置在程序根目录 app.json<br>配置文件中，详细的后面会讲到)，来指定构建使用的工具包和主题：</p>
<pre><code>&quot;builds&quot;: {
    //这里就很简单了，如果你只想用 classic 那么就注释 modern 的配置即可。
    &quot;classic&quot;: {

       &quot;toolkit&quot;: &quot;classic&quot;,

       &quot;theme&quot;: &quot;theme-triton&quot;

    },

    &quot;modern&quot;: {

       &quot;toolkit&quot;: &quot;modern&quot;,

       &quot;theme&quot;: &quot;theme-neptune&quot;

    }

}
</code></pre><p>这样做的大概路子就是，在 Ext JS 这个框架里用这两套工具包分别对应 桌面 和 移动设备。程序在构建过程中，会构建两套 UI<br>界面出来，而不是我们平时那种响应式的应用，响应式是一套 UI<br>自动根据不同设备改变布局。实际上在企业开发中，甚至互联网开发中，往往都是独立再开发一套支持移动设备的前端 UI，而 Ext JS 6<br>将此合并我认为是非常好的，这样你桌面和移动端都有一套标准，而且很多逻辑可以共享复用，不同的 view 写在独立的工具包里。维护起来也很方便。</p>
<p>如果你是一个 Ext JS 新手，看到这里肯定非常困惑，不过你现在不用担心，这些在后面都会有我们的样例代码来说明，会让你有更深的理解。</p>
<p>Ext JS 6 附带了两个主题包， classic 和 modern 主题包，这两个主题包里分别有各自支持的几种主题。</p>
<p>在 Ext JS 中的一些特定主题，提供了在 Windows，Android，iPhone<br>等设备上类似原生应用的外观。这些你将在第八章(主题和响应设计)学习。</p>
<h3 id="设置-Ext-JS-开发环境"><a href="#设置-Ext-JS-开发环境" class="headerlink" title="设置 Ext JS 开发环境"></a>设置 Ext JS 开发环境</h3><p>你需要安装一个叫 Sencha Cmd 的工具，这会让你的 Ext JS 应用开发变得简单。它可用于Windows、Mac 和 Linux。</p>
<ul>
<li>Sencha Cmd 并不是开发 Ext JS 应用必须用的，但是它会让你的工作非常轻松，所以这里强烈推荐使用 Sencha Cmd。 </li>
</ul>
<h3 id="Sencha-Cmd"><a href="#Sencha-Cmd" class="headerlink" title="Sencha Cmd"></a>Sencha Cmd</h3><p>Sencha Cmd 是在 sencha 产品下做开发时使用的命令行工具，例如开发 Ext JS ,Sencha Touch<br>应用时都可以用。它通过许多的自动化任务帮助你提高生产力。它包含以下功能，包管理，JS 编译器，构建脚本，主题等等。</p>
<p>在安装 Sencha Cmd 之前，你需要安装 JRE 环境，如果你使用的是 Sencha Cmd 5，那么你还需要安装 Ruby。这里如何安装 JRE 或<br>JDK，还是也翻译一下，因为 Sencha Cmd 使用的是 apache ant 作为构建工具，所以需要安装 JRE 环境。而在 Sencha Cmd 5<br>编译 SASS 时则需要使用 Ruby，上面说过 Ext JS 6 使用 fashion 编译 SASS 了，下载最新的 Sencha Cmd 6<br>则不需要安装 Ruby了。</p>
<h3 id="Java-运行时环境-JRE"><a href="#Java-运行时环境-JRE" class="headerlink" title="Java 运行时环境 (JRE)"></a>Java 运行时环境 (JRE)</h3><p>在命令行窗口输入以下命令检查 java 是否能正常运行在你的设备上：</p>
<pre><code>java -version
</code></pre><p>如果你已经在你的设备上安装了 java，那么你应该看到类似下面的代码；否则请下载安装 JRE 或 JDK：</p>
<p>java version “1.8.0_25”</p>
<p>Java(TM) SE Runtime Environment (build 1.8.0_25-b17)</p>
<p>Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)</p>
<p>这一步报错的话，检查一下你是否配置了 java 环境变量，不知道怎么配置请自行搜索。</p>
<h3 id="Ruby"><a href="#Ruby" class="headerlink" title="Ruby"></a>Ruby</h3><p>注意，如果你使用 Sencha Cmd 6 就不需要安装 Ruby，但是如果你正在使用 Sencha Cmd 5，那还是要安装 Ruby<br>的。输入以下命令，检查 Ruby 是否安装成功:</p>
<pre><code>ruby --version
</code></pre><p>如果已经安装完成，应该看到类似下面的代码；否则请下载安装 Ruby：</p>
<p>ruby –version</p>
<p>ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin14]</p>
<p>Ruby 也是需要配置环境变量的，这里不再介绍方法，不会的自行搜索。</p>
<h3 id="安装-Sencha-Cmd"><a href="#安装-Sencha-Cmd" class="headerlink" title="安装 Sencha Cmd"></a>安装 Sencha Cmd</h3><p>现在从 Sencha 官网上下载并安装 Sencha Cmd 。安装完成后在命令行窗口输入以下命令检查是否正常运行(同样需要配置环境变量)：</p>
<pre><code>sencha which
</code></pre><p>正常情况下应该显示类似以下的代码：</p>
<p>Sencha Cmd v6.0.0.92</p>
<p>/bin/Sencha/Cmd/6.0.0.92/</p>
<p>如果报错，你应该配置环境变量，在 Mac 电脑上运行以下命令添加安装路径到 PATH 变量：</p>
<pre><code>export PATH=~/bin/Sencha/Cmd/6.0.0.92:$PATH
</code></pre><p>在 Windows，运行以下命令添加环境变量(这一步你其实可以略过的，我想能看这篇文章的人都知道怎么做)：</p>
<pre><code>set PATH=%PATH%;C:\Sencha\Cmd\6.0.0.92
</code></pre><h3 id="用-Sencha-Cmd-生成第一个-Ext-JS-应用"><a href="#用-Sencha-Cmd-生成第一个-Ext-JS-应用" class="headerlink" title="用 Sencha Cmd 生成第一个 Ext JS 应用"></a>用 Sencha Cmd 生成第一个 Ext JS 应用</h3><p>打开命令行窗口键入以下命令：</p>
<pre><code>sencha generate app --ext MyApp ./myapp
</code></pre><p>运行上面的命令将会创建名为 MyApp 的 Ext JS 应用，应用所有的文件都放在当前目录下名为 myapp 的文件夹。</p>
<p>注意，上面的命令生成的 Ext JS 应用代码，包含两个工具包：classic 和<br>modern。因为你不明确指定需要用那个工具包的时候，默认创建的就是通用的应用。如果你需要指定使用 classic 或者 modern 工具包。那么用<br>–modern  或者  –classic  参数，如以下命令所示：</p>
<pre><code>sencha generate app --ext --modern MyApp ./myapp
</code></pre><p>当你第一次运行这个命令时，这应该会自动下载 Ext JS 6。如果没有自动下载，那你需要手动的取下载 Ext JS 6，这里贴出来 GPL 协议的 Ext<br>JS 6 官网下载地址  <a href="http://cdn.sencha.com/ext/gpl/ext-6.0.0-gpl.zip" target="_blank" rel="noopener"> http://cdn.sencha.com/ext/gpl/ext-6.0.0-gpl.zip
</a> 这里下载后解压，这时候生成 Ext JS<br>应用时就可以使用以下命令以指定 SDK 的形式生成了：</p>
<pre><code>sencha -sdk /path/to/sdk generate app MyApp /path/to/myapp
</code></pre><p>Sencha Cmd 支持 Ext JS 4.1.1a 以及更高版本，支持 Sencha Touch 2.1 以及更高版本。在你的电脑里可以有多个版本的<br>SDK 。 上面的命令是基于一个特定的 Sencha SDK 来生成的 Ext JS 应用。</p>
<p>下面的例子，在目录  /projects/extjs/myapp  下生成名为 MyApp 的 Ext JS 应用：</p>
<pre><code>sencha -sdk /bin/Sencha/ext/6.0.0/ generate app MyApp /projects/extjs/myapp
</code></pre><p>OK，现在可以查看已经创建的应用了，运行以下命令：  </p>
<pre><code>cd /projects/extjs/myapp

sencha app watch
</code></pre><p>这时会运行一些构建相关的任务，最终你将在命令行窗口看到类似下图的界面：</p>
<p><a href="http://www.jeeboot.com/wp-
content/uploads/2015/12/%E6%9C%AA%E6%A0%87%E9%A2%98-1.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-
content/uploads/2015/12/%E6%9C%AA%E6%A0%87%E9%A2%98-1.jpg" alt="未标题-1">
</a></p>
<p>上面的命令监控了任意代码修改保存后，都会在浏览器刷新时反应最新的改动。</p>
<p>在浏览器输入默认 URL (  <a href="http://localhost:1842" target="_blank" rel="noopener">http://localhost:1842</a>)  ，如图所示，看到类似以下界面：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/1.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/1.jpg" alt="1">
</a></p>
<p>默认当你使用电脑访问 URL (  <a href="http://localhost:1842" target="_blank" rel="noopener">http://localhost:1842</a>  )，应用会自动检测并为你展示 classic 工具包的<br>UI。如果访问是来自一个移动端浏览器，它将展示 modern 工具包。如何在电脑上看 modern 风格的应用呢？附加参数  (<br><a href="http://localhost:1842?profile=modern" target="_blank" rel="noopener">http://localhost:1842?profile=modern</a>  )，你将看到以下截图：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/11.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/11.jpg" alt="1">
</a></p>
<p>MyApp  应用的整体目录结构如下图。我们瞧一瞧这个实例程序都有哪些比较重要的文件。</p>
<p>应用包含了  model  ,  store  , 和  application.js  。你就把 store 看成是一个 model<br>实例的集合。store 是为你的程序功能提供并加载数据用的，你可以认为 store 就是一个数据源，它支持排序，过滤，分页等等，经常用到 store 的就是<br>grid 组件。这些都会在后面一一讲到。</p>
<p>在下面截图中,有 classci 和 modern 这两个文件夹。这两个文件夹包含使用不同工具包时写的 view(视图)代码，例如你 classic 风格的<br>view(视图) 就应该写在 classic 目录下，modern 风格的 view(视图) 就应该写在 modern 目录下。</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/12.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/12.jpg" alt="1">
</a></p>
<p>下图为 classic 和 modern 文件夹下的结构。classic 和 modern 都包含 src 目录，而你的 view(视图) 就应该写在<br>src 里面。</p>
<p>而这个  main.scss  文件是样式文件，在 classic 和 modern 工具包都存在，对应桌面和移动设备的样式。</p>
<p>还有就是在根目录也有一个  sass  文件夹，那里是放置应用所有设备共用的样式。</p>
<p>SASS  (  Syntactically Awesome Stylesheets  )是一种样式语言。Ext JS 中大量的用到<br>SASS。你将在第8章(主题和响应设计)学习到更多关于样式的知识。</p>
<p>注意 classic 和 modern 目录，这里面都不是 Ext JS 框架的工具包源码，这里是程序的代码，classic 和 modern<br>两个工具包的源码在根目录  ext  文件夹里：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/13.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/13.jpg" alt="1">
</a></p>
<p>在下一节，我将给你介绍 MVC ，并且会在 MyApp 这个应用下使用 Sencha Cmd 生成这些 MVC 相关的文件以及学习它。</p>
<h3 id="应用的体系结构"><a href="#应用的体系结构" class="headerlink" title="应用的体系结构"></a>应用的体系结构</h3><p>Ext JS 提供支持两种应用架构 MVC 和 MVVM。</p>
<h3 id="Model-模型"><a href="#Model-模型" class="headerlink" title="Model(模型)"></a>Model(模型)</h3><p>这代表着数据层。model 保存的数据可以包含数据验证和逻辑，model 经常用于 store 中。上文已经讲过，store 就是多个 model 的集合。</p>
<h3 id="View-视图"><a href="#View-视图" class="headerlink" title="View(视图)"></a>View(视图)</h3><p>这一层就是用户界面。包含有 button，form，和 message box 等等组件。</p>
<h3 id="Controller-控制器"><a href="#Controller-控制器" class="headerlink" title="Controller(控制器)"></a>Controller(控制器)</h3><p>控制器处理 view(视图)相关的逻辑，例如 view 的 event(事件)处理，还有任何程序相关逻辑都可以写在这里。</p>
<h3 id="View-model-视图模型"><a href="#View-model-视图模型" class="headerlink" title="View model (视图模型)"></a>View model (视图模型)</h3><p>view model 封装了 view(视图)所需要的展示逻辑，绑定数据到 view 并且每当数据改变时处理更新。</p>
<p>我们看一下通过 Sencha Cmd 创建的 view，controller，view model 相关的文件。</p>
<p>如果你打开  app.js  ，你将看到下列代码，这是 Ext JS 应用的启动代码：</p>
<pre><code>Ext.application({
    name : &apos;MyApp&apos;,
    extend : &apos;MyApp.Application&apos;,
    requires : [
    &apos;MyApp.view.main.Main&apos;
    ],
    mainView : &apos;MyApp.view.main.Main&apos;
});
</code></pre><p>在上面代码中，  name  定义了程序的名称，  extend  表示继承  MyApp.Application  类，这个类文件定义在  app<br>文件夹下名为  Application.js  :</p>
<p>extend: ‘MyApp.Application’</p>
<p>requires  部分指定了这个类需要的类列表。这样在  requires  里面添加的类在当前类中首次实例化时，会去先加载它，你可以把<br>requires  的作用理解为 java 中的  import  关键字。  mainView  指定的是要初始化创建的 view(视图)。</p>
<p>继续，现在查看  app  文件夹，你将看到文件  Application.js  ，和  model  ，  view  ，  store  等等。</p>
<p>下面是  Application.js  文件里的代码：</p>
<pre><code>Ext.define(&apos;MyApp.Application&apos;, {   
    extend: &apos;Ext.app.Application&apos;,
    name: &apos;MyApp&apos;,   
    stores: [
    // TODO: add global / shared stores here
    ],
    launch: function () {
        // TODO - Launch the application   
    } 
});
</code></pre><p>这里你可以看到  MyApp.Application  继承自  Ext.app.Application  。这个  launch  函数是在<br>Ext.app.Application  类里。这个函数将会在页面加载完成后调用。</p>
<p>这个 stores 是指定需要的 store 的，这里并没有用到，你将在后面的章节中学习到关于 store 的更详细的知识。</p>
<h3 id="视图模型-–-MainModel-js"><a href="#视图模型-–-MainModel-js" class="headerlink" title="视图模型 – MainModel.js"></a>视图模型 – MainModel.js</h3><p>现在看一下  \app\view\main\  目录下的  MainModel.js  文件。这个类是  Main  视图的 view<br>model(视图模型)。这个视图模型继承自  Ext.app. ViewModel  ，代码如下所示：</p>
<pre><code>Ext.define(&apos;MyApp.view.main.MainModel&apos;,{
    extend : &apos;Ext.app.ViewModel&apos;,
    alias : &apos;viewmodel.main&apos;,
    data : {
        name : &apos;MyApp&apos;,
        loremIpsum : &apos;Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&apos;
    }
});
</code></pre><h3 id="控制器-–-MainController-js"><a href="#控制器-–-MainController-js" class="headerlink" title="控制器 – MainController.js"></a>控制器 – MainController.js</h3><p>这个类是  Main  视图的控制器。在下列代码中你可以看到  onItemSelected  函数，这个函数将在视图里的 grid<br>中选中某一项时触发调用。这是什么原理呢？后面会讲到的。</p>
<pre><code>Ext.define(&apos;MyApp.view.main.MainController&apos;, {
    extend : &apos;Ext.app.ViewController&apos;,
    alias : &apos;controller.main&apos;,
    onItemSelected : function(sender, record) {
        Ext.Msg.confirm(&apos;Confirm&apos;, &apos;Are you sure?&apos;, &apos;onConfirm&apos;, this);
    },
    onConfirm : function(choice) {
        if (choice === &apos;yes&apos;) {
            //
        }
    }
});
</code></pre><p>我们可以看到 extend 继承了 Ext.app.ViewController 这个类。Ext JS 中有两种类型的控制器：<br>Ext.app.ViewController  和  Ext.app.Controller  。</p>
<p>在后面的章节中你将会学习到这两种控制器有何区别。</p>
<h3 id="视图-–-Main-js"><a href="#视图-–-Main-js" class="headerlink" title="视图 – Main.js"></a>视图 – Main.js</h3><p>如果你用的是 Sencha Cmd 5,，并且你生成应用时执行了使用哪种 toolkits 工具包，是  –modern  或  –classic<br>，如果是这样那么  Main.js  文件就在  \app\view\main  文件夹下面。但是如果你用 Sencha Cmd 6<br>生成的是通用的应用，那么将会有两个  Main.js  文件，分别在  \modern\src\view\main\  和<br>\classic\src\view\main\  目录下面。</p>
<p>在查看这两个文件的内容之前，我先通过这两个不同路径的  Main.js  文件来解释一下。在本章的上文中，你已经知道为什么 Ext JS 6 合并了 Ext<br>JS 和 Sencha Touch 为一个框架。这两个框架合并后共用一个核心，剩下的代码则分为两部分 classic 和 modern。传统的 Ext JS<br>代码移动到 classic 工具包，而 modern 的代码支持触摸和 HTML5 在 modern<br>工具包。所以这里需要两个工具包，程序会根据访问设备自动使用对应的工具包里的 UI 类(view)。</p>
<ul>
<li>应用分两个工具包并共享核心资源和逻辑，这称为通用应用。 </li>
</ul>
<p>现在我们看一下在 modern 下的  Main.js  文件：</p>
<pre><code>Ext.define(&apos;MyApp.view.main.Main&apos;, {
    extend : &apos;Ext.tab.Panel&apos;,
    xtype : &apos;app-main&apos;,
    requires : [
    &apos;Ext.MessageBox&apos;,
    &apos;MyApp.view.main.MainController&apos;,
    &apos;MyApp.view.main.MainModel&apos;,
    &apos;MyApp.view.main.List&apos;
    ],
    controller : &apos;main&apos;,
    viewModel : &apos;main&apos;,
    defaults : {
        styleHtmlContent : true
    },
    tabBarPosition : &apos;bottom&apos;,
    items : [{
        title : &apos;Home&apos;,
        iconCls : &apos;fa-home&apos;,
        layout : &apos;fit&apos;,
        items : [ {
            xtype : &apos;mainlist&apos;
        } ]
    }, {
        title : &apos;Users&apos;,
        iconCls : &apos;fa-user&apos;,
        bind : {
            html : &apos;{loremIpsum}&apos;
        }
    }, {
        title : &apos;Groups&apos;,
        iconCls : &apos;fa-users&apos;,
        bind : {
            html : &apos;{loremIpsum}&apos;
        }
    }, {
        title : &apos;Settings&apos;,
        iconCls : &apos;fa-cog&apos;,
        bind : {
            html : &apos;{loremIpsum}&apos;
        }
    }]
});
</code></pre><p>这个 Main 视图是一个 tab panel，因为它继承了  Ext.tab.Panel  。这个类有属性  controller  ，<br>viewmodel  ，  requires  配置了需要依赖的类。创建了四个 tab 页(items属性)，并且绑定了数据  ViewModel  里的<br>loremIpsum  属性。你将会在后续章节中了解关于此更多的详细信息。</p>
<p>接着看一下在  \classic\src\view\main\  下的  Main.js  文件内容：</p>
<pre><code>Ext.define(&apos;NewApp.view.main.Main&apos;, {
    extend : &apos;Ext.tab.Panel&apos;,
    xtype : &apos;app-main&apos;,
    requires : [
    &apos;Ext.plugin.Viewport&apos;,
    &apos;Ext.window.MessageBox&apos;,
    &apos;NewApp.view.main.MainController&apos;,
    &apos;NewApp.view.main.MainModel&apos;,
    &apos;NewApp.view.main.List&apos;
    ],
    controller : &apos;main&apos;,
    viewModel : &apos;main&apos;,
    ui : &apos;navigation&apos;,
    tabBarHeaderPosition : 1,
    titleRotation : 0,
    tabRotation : 0,
    header : {
        layout : {
            align : &apos;stretchmax&apos;
        },
        title : {
            bind : {
                text : &apos;{name}&apos;
            },
            flex : 0
        },
        iconCls : &apos;fa-th-list&apos;
    },
    tabBar : {
        flex : 1,
        layout : {
            align : &apos;stretch&apos;,
            overflowHandler : &apos;none&apos;
        }
    },
    responsiveConfig : {
        tall : {
            headerPosition : &apos;top&apos;
        },
        wide : {
            headerPosition : &apos;left&apos;
        }
    },
    defaults : {
        bodyPadding : 20,
        tabConfig : {
            plugins : &apos;responsive&apos;,
            responsiveConfig : {
                wide : {
                    iconAlign : &apos;left&apos;,
                    textAlign : &apos;left&apos;
                },
                tall : {
                    iconAlign : &apos;top&apos;,
                    textAlign : &apos;center&apos;,
                    width : 120
                }
            }
        }
    },
    items : [ {
        title : &apos;Home&apos;,
        iconCls : &apos;fa-home&apos;,
        items : [ {
            xtype : &apos;mainlist&apos;
        } ]
    }, {
        title : &apos;Users&apos;,
        iconCls : &apos;fa-user&apos;,
        bind : {
            html : &apos;{loremIpsum}&apos;
        }
    }, {
        title : &apos;Groups&apos;,
        iconCls : &apos;fa-users&apos;,
        bind : {
            html : &apos;{loremIpsum}&apos;
        }
    }, {
        title : &apos;Settings&apos;,
        iconCls : &apos;fa-cog&apos;,
        bind : {
            html : &apos;{loremIpsum}&apos;
        }
    } ]
});
</code></pre><p>上面代码中，items 中的代码几乎和 modern 工具包中的是一样的。此外，这个文件有些配置是专用于支持响应设计的。下列代码告诉框架使用的  ui<br>组件为  navigation  ：</p>
<p>ui: ‘navigation’</p>
<p>在第8章(主题和响应设计)中会介绍关于这个 UI 配置和响应设计的内容。</p>
<p>同样的，如果你打开 classic 或 modern 下的  List.js  ，你会发现他们只有很细微的区别。</p>
<p>探索 Sencha Cmd 命令  </p>
<p>现在让我们学习 Sencha Cmd 的一些非常有用的命令。</p>
<h3 id="Sencha-命令格式"><a href="#Sencha-命令格式" class="headerlink" title="Sencha 命令格式"></a>Sencha 命令格式</h3><p>Sencha 命令采取以下格式：</p>
<p>sencha [category] [command] [options…] [arguments…]</p>
<p>在 Sencha Cmd 中许多命令和可选项。我们看一下都有哪些比较重要的命令。</p>
<h1 id="Help"><a href="#Help" class="headerlink" title="Help"></a>Help</h1><p>键入以下命令，你将获取一个 categories(类别)列表，一个顶层的 commands(命令)列表，一个可用的 options(选项)列表：</p>
<pre><code>sencha help
</code></pre><p>获取一个特定类别的帮助信息，类别名称紧随在 help 后面，例如获取一个类别 app 的帮助信息，运行下列命令：  </p>
<pre><code>sencha help app
</code></pre><p>将产生以下输出：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/14.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/14.jpg" alt="1">
</a></p>
<p>如果你想进一步获取 app 命令下的子命令的帮助信息，你只需要在最后添加子命令例如 clean，如以下代码所示：</p>
<pre><code>sencha help app clean
</code></pre><p>将产生以下输出：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/15.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/15.jpg" alt="1">
</a></p>
<h3 id="升级-Sencha-Cmd"><a href="#升级-Sencha-Cmd" class="headerlink" title="升级 Sencha Cmd"></a>升级 Sencha Cmd</h3><p>如果你想检查是否有 Sencha Cmd 可用的更新，使用以下命令：</p>
<pre><code>sencha upgrade --check
</code></pre><p>如果你要升级 Sencha Cmd，只需要移除  –check  选项，如以下代码所示：</p>
<pre><code>sencha upgrade
</code></pre><h3 id="升级-ExtJS-应用"><a href="#升级-ExtJS-应用" class="headerlink" title="升级 ExtJS 应用"></a>升级 ExtJS 应用</h3><p>升级一个现有的 ExtJS 应用到最新版本，需要先进入到你的 ExtJS 工程目录，使用一下命令：</p>
<pre><code>sencha app upgrade 后面跟上 sdk 路径
</code></pre><p>不加路径的话，就会自动从官网下载最新版本的 ExtJS 框架。</p>
<p>注意：如果 sencha cmd 与要升级到的 ExtJS 框架版本不兼容，需要先升级 SenchaCMD 再升级 ExtJS。</p>
<h3 id="生成一个应用"><a href="#生成一个应用" class="headerlink" title="生成一个应用"></a>生成一个应用</h3><p>Sencha Cmd 支持 Ext JS 4.1.1a 及更高版本和支持 Sencha Touch 2.1 及更高版本。你电脑上可以存在多个版本的<br>SDK。这是基于 Sencha SDK 生成应用的命令格式，例如 Ext JS 或者 Sencha Touch：</p>
<pre><code>sencha -sdk /path/to/sdk generate app [--modern/classic] MyApp /path/to/ myapp
</code></pre><p>这个示例代码将在目录  /Users/SomeUser/projects/extjs/myapp  下生成名为  MyApp  的 Ext JS 6 应用<br>：</p>
<pre><code>sencha -sdk /Users/SomeUser/bin/Sencha/Cmd/repo/extract/ext/6.0.0/ generate app MyApp /Users/SomeUser/projects/extjs/myapp
</code></pre><h3 id="构建应用"><a href="#构建应用" class="headerlink" title="构建应用"></a>构建应用</h3><p>运行下列命令将进行构建 HTML，JS，SASS 等等：</p>
<pre><code>sencha app build
</code></pre><p>使用 Sencha Cmd 6 构建 Ext JS 6 应用，你还可以运行下列命令选择构建 moern 或 classic 风格的应用：  </p>
<pre><code>sencha app build modern
sencha app build classic
</code></pre><p>这里说一下，  modern  和  classic  的构建配置在  app.json  。 默认 Sencha Cmd 运行两个构建配置：<br>classic 和 modern 。如果需要你也可以在  app.json  中添加额外的构建配置。</p>
<h3 id="启动应用"><a href="#启动应用" class="headerlink" title="启动应用"></a>启动应用</h3><p>watch 命令用于重新构建并启动应用。这不仅会启动应用程序,还监视任何代码更改，一旦代码改变，浏览器刷新将包括最新的代码：</p>
<pre><code>sencha app watch
</code></pre><p>在 Sencha Cmd 6 和 Ext JS 6，你也可以运行下列命令选择 modern 或 classic：  </p>
<pre><code>sencha app watch modern
sencha app watch classic
</code></pre><h3 id="代码生成"><a href="#代码生成" class="headerlink" title="代码生成"></a>代码生成</h3><p>用Sencha Cmd，你可以生成 Ext JS 代码，例如 view，controller，model：</p>
<pre><code>sencha generate view myApp.MyView

sencha generate model MyModel id:int,fname,lname

sencha generate controller MyController
</code></pre><p>当你生成 model 时如果不指定字段类型，默认类型是 string。</p>
<h3 id="升级你的应用"><a href="#升级你的应用" class="headerlink" title="升级你的应用"></a>升级你的应用</h3><p>Sencha Cmd 升级 SDK 的版本是很容易的。使用这个升级命令将你的程序升级到新框架：</p>
<pre><code>sencha app upgrade [ path-to-new-framework ]
</code></pre><h3 id="调试-Ext-JS-应用"><a href="#调试-Ext-JS-应用" class="headerlink" title="调试 Ext JS 应用"></a>调试 Ext JS 应用</h3><p>你可以使用浏览器默认的调试器来调试 Ext JS 代码，但是使用火狐浏览器的 firebug 插件再安装 Illumination 或者使用 Chrome<br>的 Inspector 插件调试这将会容易得多。</p>
<p>Illumination</p>
<p>Illumination 是一个第三方工具。它并不是 Sencha 的一个产品，目前它只支持火狐浏览器。</p>
<h3 id="Illumination-的特性"><a href="#Illumination-的特性" class="headerlink" title="Illumination 的特性"></a>Illumination 的特性</h3><p>这有些 Illumination 的特性，这将减少你在调试上花的时间。</p>
<h3 id="对象命名"><a href="#对象命名" class="headerlink" title="对象命名"></a>对象命名</h3><p>Illumination 会很容易识别出 Ext JS 组件，所以在 illumination 标签页你能看到 Ext JS 组件名称，例如<br>Ext.panel.Panel  而不是像在 firebug 的 DOM 页里那样显示成  Object  。</p>
<h3 id="Element-高亮"><a href="#Element-高亮" class="headerlink" title="Element 高亮"></a>Element 高亮</h3><p>如果在 Illumination 窗口你鼠标悬停在任意对象上，将会突出高亮在 HTML 页面中的组件。</p>
<h3 id="上下文菜单The-contextual-menu"><a href="#上下文菜单The-contextual-menu" class="headerlink" title="上下文菜单The contextual menu"></a>上下文菜单The contextual menu</h3><p>一个 Ext JS 组件是由数个 HTML 元素组成的。如果你在页面右击选择使用 firebug 查看元素，你会看到元素是嵌套在 Ext JS<br>组件里，但是如果你选择 Illumination 来查看元素，会直接显示 Ext JS 组件，这更有便于检查组件的方法属性和事件。</p>
<p>firebug  DOM  标签页里对象是如何展示的：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/16.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/16.jpg" alt="1">
</a></p>
<p>再看一下 Illumination 标签页下对象是如何展示的，你会发现所有组件都显示在下列截图中：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/17.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/17.jpg" alt="1">
</a></p>
<p>尽管 Illumination 使调试 Ext JS 应用变得容易，但是并不是必须用它。它并不是免费的，你不想购买的话，仍然可以使用 firebug<br>调试，但你也许会多花一些时间调试，或者使用 App Inspector 插件或者 Sencha Fillde 调试。然并卵，我还是建议使用 firebug<br>就行了。</p>
<h3 id="App-Inspector"><a href="#App-Inspector" class="headerlink" title="App Inspector"></a>App Inspector</h3><p>App Inspector 是一个由 Sencha 开发的免费的 Chrome 插件。它支持所有 Illumination 支持的功能。</p>
<p>相比使用 Illumination 一些信息在 App Inspector 查找更容易方便，并且使用 Illumination 比使用 App<br>Inspector 载入程序时间更长。</p>
<p>App Inspector 的截图：  </p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/18.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/18.jpg" alt="1">
</a></p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/19.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/19.jpg" alt="1">
</a></p>
<h3 id="Sencha-Fiddle"><a href="#Sencha-Fiddle" class="headerlink" title="Sencha Fiddle"></a>Sencha Fiddle</h3><p>这是另外的一个调试工具。这也是一个在线的基于 web 的 IDE 并提供了一些调试功能，如下图：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/110.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/110.jpg" alt="1">
</a></p>
<p>开发 IDE</p>
<p>尽管你可以使用任何简单的文本编辑器来编写 Ext JS 代码，使用 IDE 开发肯定更简单。Sencha 为JetBrains 提供 Sencha<br>JetBrains 插件支持，例如  IntelliJ，WebStrome，PHPStorm，和 RubyMine。</p>
<p>如果你在寻找一些免费的 IDE，那么你可以看一下 Visual Studio Code 和 Brackets.io<br>。这两个都是非常轻量级，并且开源的文本编辑器，支持 Mac，Windows，Linux，下图是  Visual Studio Code  ：</p>
<p><a href="http://www.jeeboot.com/wp-content/uploads/2015/12/111.jpg" target="_blank" rel="noopener"> <img src="http://www.jeeboot.com/wp-content/uploads/2015/12/111.jpg" alt="1">
</a></p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>在本章中, 我们看到了使用 JavaScript 框架相较于使用传统纯 JavaScript 的优势。也了解了 Ext JS<br>应用的体系结构，你也学习到如何配置你的开发环境以及如何用 Sencha Cmd 搭建 Ext JS 应用。</p>
<p>在下一章中, 您将了解Ext JS的核心概念和基本知识。</p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/Ext JS 6学习文档–第1章–ExtJS入门指南/#comments" id="sourceId::2018/04/20/Ext JS 6学习文档–第1章–ExtJS入门指南/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-Docker私有仓库的搭建与使用" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/Docker私有仓库的搭建与使用/">Docker私有仓库的搭建与使用</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/Docker私有仓库的搭建与使用/">
            <time datetime="2018-04-19T16:34:21.416Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/docker/">docker</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>和Maven一样，Docker不仅提供了一个中央仓库，同时也允许我们搭建私有仓库。如果读者对Maven有所了解，将会很容易理解私有仓库的优势：</p>
<ul>
<li>节省带宽，镜像无需从中央仓库下载，只需从私有仓库中下载即可 </li>
<li>对于私有仓库中已有的镜像，提升了下载速度 </li>
<li>便于内部镜像的统一管理 </li>
</ul>
<p>下面我们来讲解一下如何搭建、使用私有仓库</p>
<h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><p>准备两台安装有Docker的CentOS7的机器，主机规划如下（仅供参考）：</p>
<p>主机  IP  角色</p>
<p>node0</p>
<p>192.168.11.143</p>
<p>Docker开发机</p>
<p>node1</p>
<p>192.168.11.144</p>
<p>Docker私有仓库</p>
<h2 id="安装、使用私有仓库"><a href="#安装、使用私有仓库" class="headerlink" title="安装、使用私有仓库"></a>安装、使用私有仓库</h2><p>网上有很多 <code>docker-registry</code> 的教程，但是 <code>docker-registry</code> 已经过时，并且已经2年不维护了。详见 <a href="https://github.com/docker/docker-
registry" target="_blank" rel="noopener"><br>https://github.com/docker/docker-registry </a> ，故而本文不做探讨，对 <code>docker-registry</code> 有兴趣的童鞋可以查阅本节的参考文档。</p>
<p>本节讲解registry V2，registry V2需要Docker版本高于1.6.0。registry<br>V2要求使用https访问，那么我们先做一些准备，为了方便，这边模拟以域名 <code>reg.itmuch.com</code> 进行讲解。</p>
<h3 id="使用域名搭建https的私有仓库"><a href="#使用域名搭建https的私有仓库" class="headerlink" title="使用域名搭建https的私有仓库"></a>使用域名搭建https的私有仓库</h3><ul>
<li>首先修改两台机器的hosts，配置 <code>192.168.11.144</code> 到 <code>reg.itmuch.com</code> 的映射 </li>
</ul>
<pre><code>echo &apos;192.168.11.144 reg.itmuch.com&apos;&gt;&gt; /etc/hosts
</code></pre><ul>
<li>既然使用https，那么我们需要生成证书，本文讲解的是使用openssl自签名证书，当然也可以使用诸如 <code>Let’s Encrypt</code> 等工具生成证书，首先在node1机器上生成key： </li>
</ul>
<pre><code>mkdir -p ~/certs
cd ~/certs
openssl genrsa -out reg.itmuch.com.key 2048
</code></pre><p>再生成密钥文件：  </p>
<pre><code>openssl req -newkey rsa:4096 -nodes -sha256 -keyout reg.itmuch.com.key -x509 -days 365 -out reg.itmuch.com.crt
</code></pre><p>会有一些信息需要填写：  </p>
<pre><code>Country Name (2 letter code) [XX]:CN                                    # 你的国家名称
State or Province Name (full name) []:JS                                # 省份
Locality Name (eg, city) [Default City]:NJ                                # 所在城市
Organization Name (eg, company) [Default Company Ltd]:ITMUCH            # 组织名称
Organizational Unit Name (eg, section) []:ITMUCH                        # 组织单元名称
Common Name (eg, your name or your server&apos;s hostname) []:reg.itmuch.com # 域名
Email Address []:eacdy0000@126.com                                        # 邮箱
</code></pre><p>这样自签名证书就制作完成了。</p>
<ul>
<li>由于是自签名证书，默认是不受Docker信任的，故而需要将证书添加到Docker的根证书中，Docker在CentOS 7中，证书存放路径是 <code>/etc/docker/certs.d/域名</code> ： </li>
</ul>
<p><code>node1</code> 端：</p>
<pre><code>mkdir -p /etc/docker/certs.d/reg.itmuch.com
cp ~/certs/reg.itmuch.com.crt /etc/docker/certs.d/reg.itmuch.com/
</code></pre><p><code>node0</code> 端：将生成的证书下载到根证书路径</p>
<pre><code>mkdir -p /etc/docker/certs.d/reg.itmuch.com
scp root@192.168.11.144:/root/certs/reg.itmuch.com.crt /etc/docker/certs.d/reg.itmuch.com/
</code></pre><ul>
<li>重新启动 <code>node0</code> 和 <code>node1</code> 的Docker </li>
</ul>
<pre><code>service docker restart
</code></pre><ul>
<li>在 <code>node1</code> 上启动私有仓库 </li>
</ul>
<p>首先切换到家目录中，这一步不能少，原因是下面的-v 挂载了证书，如果不切换，将会引用不到证书文件。</p>
<pre><code>cd ~
</code></pre><p>启动Docker私有仓库（注意：如果直接粘贴运行，请删除掉注释）：  </p>
<pre><code>docker run -d -p 443:5000 --restart=always --name registry \
  -v `pwd`/certs:/certs \                                # 将“当前目录/certs”挂载到容器的“/certs”
  -v /opt/docker-image:/opt/docker-image \                            
  -e STORAGE_PATH=/opt/docker-image \                                # 指定容器内存储镜像的路径
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/reg.itmuch.com.crt \        # 指定证书文件
  -e REGISTRY_HTTP_TLS_KEY=/certs/reg.itmuch.com.key \                # 指定key文件
  registry:2
</code></pre><p>其中，之所以挂载/opt/docker-image目录，是为了防止私有仓库容器被删除，私有仓库中的镜像也会丢失。</p>
<ul>
<li>在 <code>node0</code> 上测试，将镜像push到私服 </li>
</ul>
<pre><code>docker pull kitematic/hello-world-nginx
docker tag kitematic/hello-world-nginx reg.itmuch.com/kitematic/hello-world-nginx # 为本地镜像打标签
docker push reg.itmuch.com/kitematic/hello-world-nginx            # 将镜像push到私服
</code></pre><p>会发现如下内容：  </p>
<pre><code>The push refers to a repository [reg.itmuch.com/kitematic/hello-world-nginx]
5f70bf18a086: Pushed 
b51acdd3ef48: Pushed 
3f47ff454588: Pushed 
....
latest: digest: sha256:d3e1883b703c39556f2f09da14cc3b820f69a43436655c882c0c0ded0dda6a4b size: 3226
</code></pre><p>说明已经push成功。</p>
<ul>
<li>从私服中下载镜像： </li>
</ul>
<pre><code>docker pull reg.itmuch.com/kitematic/hello-world-nginx
</code></pre><h3 id="配置登录认证"><a href="#配置登录认证" class="headerlink" title="配置登录认证"></a>配置登录认证</h3><p>在很多场景下，我们需要用户登录后才能访问私有仓库，那么我们可以如下操作：</p>
<p>建立在上文生成证书，同时重启过Docker服务的前提下，我们讲解一下如何配置：</p>
<ul>
<li>为防止端口冲突，我们首先删除或停止之前启动好的私有仓库： </li>
</ul>
<pre><code>docker kill registry
</code></pre><ul>
<li>在node1机器上安装 <code>httpd-tools</code> ： </li>
</ul>
<pre><code>yum install httpd-tools
</code></pre><ul>
<li>在node机器上创建密码文件，并添加一个用户 <code>testuser</code> ，密码是 <code>testpassword</code> ： </li>
</ul>
<pre><code>cd ~
mkdir auth
htpasswd -Bbn testuser testpassword &gt; auth/htpasswd
</code></pre><ul>
<li>在node1机器上切换到 <code>~</code> 目录，并启动私有仓库（注意：如果直接粘贴运行，请删除掉注释）： </li>
</ul>
<pre><code>docker run -d -p 443:5000 --restart=always --name registry2 \
  -v /opt/docker-image:/var/lib/registry \                            # 挂载容器内存储镜像路径到宿主机
  -v `pwd`/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/reg.itmuch.com.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/reg.itmuch.com.key \
  -v `pwd`/auth:/auth \
  -e &quot;REGISTRY_AUTH=htpasswd&quot; \
  -e &quot;REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm&quot; \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  registry:2
</code></pre><ul>
<li>测试： </li>
</ul>
<pre><code>docker push reg.itmuch.com/kitematic/hello-world-nginx
</code></pre><p>提示：  </p>
<pre><code>461f75075df2: Image push failed 
no basic auth credentials
</code></pre><p>说明需要认证。</p>
<p>我们登陆一下，执行：</p>
<pre><code>docker login reg.imuch.com
</code></pre><p>再次执行  </p>
<pre><code>docker push reg.itmuch.com/kitematic/hello-world-nginx
</code></pre><p>就可以正常push镜像到私有仓库了。</p>
<p>注意：  如果想要从私有仓库上下载镜像，同样需要登录。</p>
<h2 id="参考文档："><a href="#参考文档：" class="headerlink" title="参考文档："></a>参考文档：</h2><p>官方文档： <a href="https://docs.docker.com/registry/deploying/#/running-a-domain-registry" target="_blank" rel="noopener"> https://docs.docker.com/registry/deploying/#/running-a-domain-registry
</a></p>
<p>Docker Registry V2 htpasswd认证方式搭建： <a href="http://www.tuicool.com/articles/vMZZveM" target="_blank" rel="noopener"> http://www.tuicool.com/articles/vMZZveM
</a></p>
<p>Docker Registry V2搭建： <a href="http://www.tuicool.com/articles/6jEJZj" target="_blank" rel="noopener"> http://www.tuicool.com/articles/6jEJZj
</a></p>
<p>Docker Registry V2搭建： <a href="http://tomhat.iteye.com/blog/2304098" target="_blank" rel="noopener"> http://tomhat.iteye.com/blog/2304098
</a></p>
<p>Docker Registry V1搭建： <a href="http://blog.csdn.net/wsscy2004/article/details/26279569" target="_blank" rel="noopener"><br>http://blog.csdn.net/wsscy2004/article/details/26279569
</a></p>
<p>非认证的Docker Registry V1搭建： <a href="http://blog.csdn.net/wangtaoking1/article/details/44180901" target="_blank" rel="noopener"><br>http://blog.csdn.net/wangtaoking1/article/details/44180901
</a></p>
<p>带认证的Docker Registry V1搭建： <a href="http://snoopyxdy.blog.163.com/blog/static/601174402015823741997/" target="_blank" rel="noopener"><br>http://snoopyxdy.blog.163.com/blog/static/601174402015823741997/
</a></p>
<p>Docker专题汇总： <a href="http://www.zimug.com/360.html" target="_blank" rel="noopener"> http://www.zimug.com/360.html </a></p>
<p>Docker疑难解答： <a href="https://segmentfault.com/q/1010000000938076" target="_blank" rel="noopener"> https://segmentfault.com/q/1010000000938076
</a></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/Docker私有仓库的搭建与使用/#comments" id="sourceId::2018/04/20/Docker私有仓库的搭建与使用/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-Excel文件office2003 and office2007 操作" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/Excel文件office2003 and office2007 操作/">Excel文件office2003 and office2007 操作</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/Excel文件office2003 and office2007 操作/">
            <time datetime="2018-04-19T16:34:21.416Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/J2EE/">J2EE</a>
    </div>

                        
                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>这篇blog主要是讲述java中poi读取excel，而excel的版本包括：2003-2007和2010两个版本，<br>即excel的后缀名为：xls和xlsx。</p>
<p>poi-3.7-20101029.jar，下载地址：<a href="http://download.csdn.net/detail/evangel_z/4107089" target="_blank" rel="noopener">http://download.csdn.net/detail/evangel_z/4107089</a><br>geronimo-stax-<br>api_1.0_spec-1.0.jar，下载地址：<a href="http://download.csdn.net/detail/evangel_z/4107083" target="_blank" rel="noopener">http://download.csdn.net/detail/evangel_z/4107083</a><br>xmlbeans-2.3.0.jar，下载地址：<a href="http://download.csdn.net/detail/evangel_z/4107140" target="_blank" rel="noopener">http://download.csdn.net/detail/evangel_z/4107140</a><br>poi-<br>ooxml-3.7-20101029.jar，下载地址：<a href="http://download.csdn.net/detail/evangel_z/4107145" target="_blank" rel="noopener">http://download.csdn.net/detail/evangel_z/4107145</a><br>poi-ooxml-schemas-3.7-20101029.jar，下载地址：<a href="http://download.csdn.net/detail/evange" target="_blank" rel="noopener">http://download.csdn.net/detail/evange</a><br>l_z/4108997<br>以上5个jar，就可读取Excel 2003；  </p>
<h2 id="—————————————————————————"><a href="#—————————————————————————" class="headerlink" title="-—————————————————————————-"></a>-—————————————————————————-</h2><p>读取Excel<br>2007，请加上dom4j-1.6.1.jar，下载地址：<a href="http://download.csdn.net/detail/evangel_z/6739735" target="_blank" rel="noopener">http://download.csdn.net/detail/evangel_z/6739735</a>  </p>
<h2 id="—————————————————————————-1"><a href="#—————————————————————————-1" class="headerlink" title="-—————————————————————————-"></a>-—————————————————————————-</h2><p>poi-3.6-20091214.jar，下载地址：<a href="http://download.csdn.net/detail/evangel_z/3895051" target="_blank" rel="noopener">http://download.csdn.net/detail/evangel_z/3895051</a><br>poi-contrib-3.6-20091214.jar，下载地址：<a href="http://download.csdn.net/detail/evangel_z/41" target="_blank" rel="noopener">http://download.csdn.net/detail/evangel_z/41</a><br>07197<br>poi-<br>scratch.6-20091214.jar，下载地址：<a href="http://download.csdn.net/detail/evangel_z/4107204" target="_blank" rel="noopener">http://download.csdn.net/detail/evangel_z/4107204</a></p>
<p>=================================================</p>
<p>源码部分： 读取操作  ReadExcelUtil  </p>
<p>=================================================</p>
<pre><code>  1 import java.io.File;
  2 import java.io.FileInputStream;
  3 import java.io.FileNotFoundException;
  4 import java.io.IOException;
  5 import java.text.DecimalFormat;
  6 import java.text.SimpleDateFormat;
  7 import java.util.ArrayList;
  8 import java.util.LinkedList;
  9 import java.util.List;
 10 
 11 import org.apache.poi.hssf.usermodel.*;
 12 import org.apache.poi.xssf.usermodel.XSSFCell;
 13 import org.apache.poi.xssf.usermodel.XSSFRow;
 14 import org.apache.poi.xssf.usermodel.XSSFSheet;
 15 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 16 
 17 /**
 18  * 读Excel文件office2003 and office2007
 19  * @author lerry.li
 20  * @date 2016-9-2
 21  */
 22 public class ReadExcelUtil {
 23 
 24     private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ReadExcelUtil.class);
 25     static DecimalFormat df = new DecimalFormat(&quot;0&quot;);// 格式化 number String 字符
 26     static SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);// 格式化日期字符串
 27     static DecimalFormat nf = new DecimalFormat(&quot;0.00&quot;);// 格式化数字
 28 
 29     /**
 30      * 对外提供读取excel 的方法
 31      * @param file
 32      * @return
 33      * @throws java.io.IOException
 34      * @throws java.io.IOException
 35      */
 36     public static List&lt;List&lt;Object&gt;&gt; readExcel(File file) throws IOException {
 37         String fileName = file.getName();
 38         String extension = fileName.lastIndexOf(&quot;.&quot;) == -1 ? &quot;&quot; : fileName
 39                 .substring(fileName.lastIndexOf(&quot;.&quot;) + 1);
 40         if (&quot;xls&quot;.equals(extension)) {
 41             return read2003Excel(file);
 42         } else if (&quot;xlsx&quot;.equals(extension)) {
 43             return read2007Excel(file);
 44         } else {
 45             throw new IOException(&quot;不支持的文件类型&quot;);
 46         }
 47     }
 48 
 49     /**
 50      * 读取 office 2003 excel
 51      * @throws java.io.IOException
 52      * @throws java.io.FileNotFoundException
 53      */
 54     @SuppressWarnings(&quot;unused&quot;)
 55     private static List&lt;List&lt;Object&gt;&gt; read2003Excel(File file)
 56             throws IOException {
 57         List&lt;List&lt;Object&gt;&gt; list = new LinkedList&lt;List&lt;Object&gt;&gt;();
 58         HSSFWorkbook hwb = new HSSFWorkbook(new FileInputStream(file));
 59         HSSFSheet sheet = hwb.getSheetAt(0);
 60         Object value = null;
 61         HSSFRow row = null;
 62         HSSFCell cell = null;
 63         for (int i = sheet.getFirstRowNum(); i &lt;= sheet
 64                 .getPhysicalNumberOfRows(); i++) {
 65             row = sheet.getRow(i);
 66             if (row == null) {
 67                 continue;
 68             }
 69             List&lt;Object&gt; linked = new LinkedList&lt;Object&gt;();
 70             for (int j = row.getFirstCellNum(); j &lt;= row.getLastCellNum(); j++) {
 71                 cell = row.getCell(j);
 72                 if (cell == null) {
 73                     continue;
 74                 }
 75                 DecimalFormat df = new DecimalFormat(&quot;0&quot;);// 格式化 number String
 76                 // 字符
 77                 SimpleDateFormat sdf = new SimpleDateFormat(
 78                         &quot;yyyy-MM-dd HH:mm:ss&quot;);// 格式化日期字符串
 79                 DecimalFormat nf = new DecimalFormat(&quot;0&quot;);// 格式化数字
 80                 switch (cell.getCellType()) {
 81                 case HSSFCell.CELL_TYPE_STRING:
 82                     value = cell.getStringCellValue();
 83                     break;
 84                 case HSSFCell.CELL_TYPE_NUMERIC:
 85                     if (&quot;@&quot;.equals(cell.getCellStyle().getDataFormatString())) {
 86                         value = df.format(cell.getNumericCellValue());
 87                     } else if (&quot;General&quot;.equals(cell.getCellStyle()
 88                             .getDataFormatString())) {
 89                         value = nf.format(cell.getNumericCellValue());
 90                     } else {
 91                         value = sdf.format(HSSFDateUtil.getJavaDate(cell
 92                                 .getNumericCellValue()));
 93                     }
 94                     break;
 95                 case HSSFCell.CELL_TYPE_BOOLEAN:
 96                     value = cell.getBooleanCellValue();
 97                     break;
 98                 case HSSFCell.CELL_TYPE_BLANK:
 99                     value = &quot;&quot;;
100                     break;
101                 default:
102                     value = cell.toString();
103                 }
104                 //if (value == null || &quot;&quot;.equals(value)) {
105                 //    continue;
106                 //}
107                 if (value == null) {
108                     continue;
109                 }
110                 linked.add(value);
111             }
112             list.add(linked);
113         }
114         return list;
115     }
116 
117     /**
118      * 读取Office 2007 excel
119      * @param file
120      * @return
121      * @throws java.io.IOException
122      */
123     private static List&lt;List&lt;Object&gt;&gt; read2007Excel(File file)
124             throws IOException {
125         int flag=0;
126         List&lt;List&lt;Object&gt;&gt; list = new ArrayList&lt;List&lt;Object&gt;&gt;();
127         // 构造 XSSFWorkbook 对象，strPath 传入文件路径
128         XSSFWorkbook xwb = new XSSFWorkbook(new FileInputStream(file));
129         // 读取第一章表格内容
130         XSSFSheet sheet = xwb.getSheetAt(0);
131         Object value = null;
132         XSSFRow row = null;
133         XSSFCell cell = null;
134         for (int i = sheet.getFirstRowNum(); i &lt;= sheet
135                 .getPhysicalNumberOfRows(); i++) {
136             flag++;
137             row = sheet.getRow(i);
138             if (row == null) {
139                 continue;
140             }
141             List&lt;Object&gt; linked = new LinkedList&lt;Object&gt;();
142             for (int j = row.getFirstCellNum(); j &lt;= row.getLastCellNum(); j++) {
143                 cell = row.getCell(j);
144                 if (cell == null) {
145                     continue;
146                 }
147                 //DecimalFormat df = new DecimalFormat(&quot;0&quot;);// 格式化 number String
148                 // 字符
149                 switch (cell.getCellType()) {
150                 case HSSFCell.CELL_TYPE_STRING:
151                     value = cell.getStringCellValue();
152                     break;
153                 case HSSFCell.CELL_TYPE_NUMERIC:
154                     if (&quot;@&quot;.equals(cell.getCellStyle().getDataFormatString())) {
155                         value = df.format(cell.getNumericCellValue());
156                     } else if (&quot;General&quot;.equals(cell.getCellStyle()
157                             .getDataFormatString())) {
158                         value = nf.format(cell.getNumericCellValue());
159                     } else {
160                         value = sdf.format(HSSFDateUtil.getJavaDate(cell
161                                 .getNumericCellValue()));
162                     }
163                     break;
164                 case HSSFCell.CELL_TYPE_BOOLEAN:
165                     value = cell.getBooleanCellValue();
166                     break;
167                 case HSSFCell.CELL_TYPE_BLANK:
168                     value = &quot;&quot;;
169                     break;
170                 default:
171                     value = cell.toString();
172                 }
173                 //if (value == null || &quot;&quot;.equals(value)) {
174                 //    continue;
175                 //}
176                 if (value == null) {
177                     continue;
178                 }
179                 linked.add(value);
180             }
181             list.add(linked);
182         }
183         return list;
184     }
185     /**
186      * 获取行数2003
187      * @param file
188      * @return 行数
189      */
190     public static int getRow2003(File file){
191         HSSFWorkbook hwb = null;
192         try {
193             FileInputStream f =  new FileInputStream(file);
194             hwb = new HSSFWorkbook(f);
195         } catch (FileNotFoundException e) {
196             e.printStackTrace();
197         } catch (IOException e) {
198             e.printStackTrace();
199         }
200         HSSFSheet sheet = hwb.getSheetAt(0);
201         return sheet.getPhysicalNumberOfRows();
202     }
203     /**
204      * 获取行数2007
205      * @param file
206      * @return
207      */
208     public static int getRow2007(File file){
209         XSSFWorkbook xwb = null;
210         try {
211             FileInputStream f = new FileInputStream(file);
212             xwb = new XSSFWorkbook(f);
213         } catch (Exception e) {
214             e.printStackTrace();
215         }
216         XSSFSheet sheet = xwb.getSheetAt(0);
217         return sheet.getPhysicalNumberOfRows();
218     }
219     
220     public static void main(String[] args) {
221         
222         File f = new File(&quot;C:/Users/Administrator/Desktop/Book1.xlsx&quot;);
223         try {
224             List&lt;List&lt;Object&gt;&gt; list = readExcel(f);
225             for (int i = 0; i &lt; list.size(); i++) {
226                 String s1 = list.get(i).toString().trim();
227                 s1 = s1.replace(&quot;[&quot;, &quot;&quot;);
228                 s1 = s1.replace(&quot;]&quot;, &quot;&quot;);
229                 s1 = s1.replace(&quot;  &quot;, &quot;&quot;); 
230                 String str = s1.replaceAll(&quot; &quot;, &quot;&quot;).trim();
231                 String[] s=str.split(&quot;,&quot;);
232                 for(int j=0;j&lt;s.length;j++){
233                     System.out.println(&quot;===&quot;+s[j]);
234                 }
235             }
236         } catch (Exception e) {
237             e.printStackTrace();
238         }
239     }
240 
241 }
</code></pre><p>=================================================</p>
<p>源码部分： 下载操作  DownLoadExcelUtil  </p>
<p>=================================================</p>
<pre><code> 1 import java.io.BufferedInputStream;
 2 import java.io.BufferedOutputStream;
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.IOException;
 6 import java.io.InputStream;
 7 import java.io.OutputStream;
 8 
 9 import javax.servlet.ServletContext;
10 import javax.servlet.ServletOutputStream;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13 
14 import org.slf4j.Logger;
15 
16 /**
17  * 读Excel文件office2003 and office2007
18  * @author lerry.li
19  * @date 2016-9-2
20  */
21 public class DownLoadExcelUtil {
22 
23     private static final Logger log = org.slf4j.LoggerFactory.getLogger(DownLoadExcelUtil.class);
24     /**
25      * 下载Excel
26      * @param path 文件路径
27      * @param response
28      */
29     public static void download(String path, HttpServletResponse response) {  
30         try {  
31             // path是指欲下载的文件的路径。  
32             File file = new File(path);  
33             // 取得文件名。  
34             String filename = file.getName();  
35             // 以流的形式下载文件。  
36             InputStream fis = new BufferedInputStream(new FileInputStream(path));  
37             byte[] buffer = new byte[fis.available()];  
38             fis.read(buffer);  
39             fis.close();  
40             // 清空response  
41             response.reset();  
42             // 设置response的Header  
43             response.addHeader(&quot;Content-Disposition&quot;, &quot;attachment;filename=&quot;  
44                     + new String(filename.getBytes()));  
45             response.addHeader(&quot;Content-Length&quot;, &quot;&quot; + file.length());  
46             OutputStream toClient = new BufferedOutputStream(  
47                     response.getOutputStream());  
48             response.setContentType(&quot;application/vnd.ms-excel;charset=gb2312&quot;);  
49             toClient.write(buffer);  
50             toClient.flush();  
51             toClient.close();  
52         } catch (Exception ex) {  
53             log.info(ex.getMessage());
54             ex.printStackTrace();  
55         }  
56     } 
57     
58 }
</code></pre>
        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/Excel文件office2003 and office2007 操作/#comments" id="sourceId::2018/04/20/Excel文件office2003 and office2007 操作/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-apache调优隐藏版本信息及404重定向" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/apache调优隐藏版本信息及404重定向/">apache调优隐藏版本信息及404重定向</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/apache调优隐藏版本信息及404重定向/">
            <time datetime="2018-04-19T16:34:21.415Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/Linux/">Linux</a>
    </div>

                        
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/Apache/">Apache</a>, <a class="tag-link" href="/tags/linux/">linux</a>
    </div>

                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <p>apache调优隐藏版本信息及404重定向</p>
<p><strong> 本节所讲  内容：  </strong>   </p>
<p><strong> 实战：源码编译安装  apache  及调优技巧  </strong></p>
<hr>
<p><strong> 实战环境：  </strong></p>
<p><strong> 生产环境中，部署了  apache  之后，我们应该从安全还是性能角度，在  apache  服务上线之前，对其做诸多的优化调试才行。  </strong></p>
<hr>
<p><strong> Apache  </strong></p>
<p><strong> 实验环境：  </strong></p>
<p><strong> apache  版本：  httpd-2.2.31  </strong></p>
<p><strong> 源码包存放位置：  /usr/local/src  </strong></p>
<p><strong> 源码包编译安装位置：  apache  ：  /usr/local/apache2.2-xuegod/  </strong></p>
<hr>
<p><strong> 技巧  1  ：  </strong></p>
<p><strong> 安装  apache  屏蔽  apache  版本等敏感信息  </strong></p>
<hr>
<p><strong> 查看  apache  版本信息：  </strong></p>
<p><strong> [root@xuegod63 ~]# curl -I<a href="http://www.baidu.com" target="_blank" rel="noopener">www.baidu.com</a>  </strong></p>
<p><strong> 。。。  </strong></p>
<p><strong> X-Powered-By: HPHP  </strong></p>
<p><strong> Server:  BWS/1.1  </strong></p>
<p><strong> X-UA-Compatible:IE=Edge,chrome=1  </strong></p>
<p><strong> BDPAGETYPE: 1  </strong></p>
<p><strong> BDQID: 0xcfd31d8200026e11  </strong></p>
<p><strong> BDUSERID: 0  </strong></p>
<hr>
<p><strong> 测试自己的网站看是什么版本？有没有相应的漏洞  </strong></p>
<hr>
<p><strong> [root@xuegod63 ~]# curl -I<a href="http://www.taobao.com" target="_blank" rel="noopener">www.taobao.com</a>  </strong></p>
<p><strong> HTTP/1.1 302 Found  </strong></p>
<p><strong> Server  :Tengine  基于  nginx  做了二次开发  </strong></p>
<p><strong> Date: Tue, 17 Nov 201502:22:06 GMT  </strong></p>
<p><strong> Content-Type: text/html  </strong></p>
<p><strong> Content-Length: 258  </strong></p>
<p><strong> Connection: keep-alive  </strong></p>
<p><strong> Location: <a href="https://www.taobao.com/" target="_blank" rel="noopener"> https://www.taobao.com/ </a> </strong></p>
<p><strong> 彻底让版本等敏感信息消失  </strong></p>
<p><strong> 要彻底将版本之类的信息进行改头换面，需要在编译之前修改源码包下  include/ap_release.h  文件  </strong></p>
<p><strong> [root@xuegod63 httpd-2.2.25]#pwd  </strong></p>
<p><strong> /usr/local/src  </strong></p>
<p><strong> [root@xuegod63 src]# rm -rfhttpd-2.2.25  </strong></p>
<p><strong> [root@xuegod63 src]# tar zxfhttpd-2.2.25.tar.gz  </strong></p>
<p><strong> [root@xuegod63 src]# cdhttpd-2.2.25  </strong></p>
<p><strong> [root@xuegod63 httpd-2.2.25]#vim include/ap_release.h  #  修改源码中的版本信息为自己公司的相关信息，隐藏真实版本信息  </strong></p>
<p><strong> 改：  </strong></p>
<p><strong> 42 #define AP_SERVER_BASEVENDOR”Apache Software Foundation”  </strong></p>
<p><strong> 43 #define AP_SERVER_BASEPROJECT “ApacheHTTP Server”  </strong></p>
<p><strong> 44 #define AP_SERVER_BASEPRODUCT”Apache”  </strong></p>
<p><strong> 45  </strong></p>
<p><strong> 46 #define AP_SERVER_MAJORVERSION_NUMBER 2  </strong></p>
<p><strong> 47 #define AP_SERVER_MINORVERSION_NUMBER 2  </strong></p>
<p><strong> 48 #define AP_SERVER_PATCHLEVEL_NUMBER   25  </strong></p>
<p><strong> 49 #define AP_SERVER_DEVBUILD_BOOLEAN    0  </strong></p>
<p><strong> 为：  </strong></p>
<p><strong> #defineAP_SERVER_BASEVENDOR “xuegod”  </strong></p>
<p><strong> #defineAP_SERVER_BASEPROJECT “  </strong> <strong> web server”  </strong></p>
<p><strong> #defineAP_SERVER_BASEPRODUCT “  </strong> <strong> xuegod web server”  </strong></p>
<hr>
<p><strong> #defineAP_SERVER_MAJORVERSION_NUMBER 8  </strong></p>
<p><strong> #defineAP_SERVER_MINORVERSION_NUMBER 1  </strong></p>
<p><strong> #defineAP_SERVER_PATCHLEVEL_NUMBER   2  </strong></p>
<p><strong> #defineAP_SERVER_DEVBUILD_BOOLEAN    3  </strong></p>
<p><strong> 注释：  </strong></p>
<p><strong> #define AP_SERVER_BASEVENDOR”Apache Software Foundation” #  服务的供应商名称  </strong></p>
<p><strong> #define AP_SERVER_BASEPROJECT”Apache HTTP Server”  #  服务的项目名称  </strong></p>
<p><strong> #define AP_SERVER_BASEPRODUCT”Apache”        #  服务的产品名  </strong></p>
<p><strong> #define AP_SERVER_MAJORVERSION_NUMBER2  #  主要版本号  </strong></p>
<p><strong> #defineAP_SERVER_MINORVERSION_NUMBER 4  #  小版本号  </strong></p>
<p><strong> #defineAP_SERVER_PATCHLEVEL_NUMBER  6  #  补丁级别  </strong></p>
<p><strong> #defineAP_SERVER_DEVBUILD_BOOLEAN  0  #  </strong></p>
<p><strong> 注：上述列出的行，大家可以修改成自己想要的，然后编译安装之后，再对  httpd-default.conf  文件进行修改，对方就彻底不知道你的版本号了。  </strong></p>
<p><strong> 源码编译安装  apache  </strong></p>
<p><strong> [root@xuegod63 httpd-2.2.11]# yum installopenssl*  </strong></p>
<p><strong> [root@xuegod63 httpd-2.2.25]#./configure –prefix=/usr/local/apache2.2-xuegod –enable-so –enable-rewrite–enable-ssl   –enable-deflate  –enable-expires  #  检查安装环境并生成  Makefile  文件  </strong></p>
<hr>
<p><strong> 配置参数用途：  </strong></p>
<p><strong> --prefix=/usr/local/apache2.2   #  指定安装路径  </strong></p>
<p><strong> --enable-so  #  支持动态加载模  块  </strong></p>
<p><strong> --enable-rewrite #  支持网站地址重写  </strong></p>
<p><strong> --enable-ssl #  支持  ssl  加密  </strong></p>
<p><strong> --enable-deflate #  支持页面传输前进行压缩  </strong></p>
<p><strong> --enable-expires #  支持设置网页缓存的时间  </strong></p>
<hr>
<p><strong> 编译安装：  make&amp;&amp; make install  </strong></p>
<hr>
<p><strong> 查看安装后目录：  </strong></p>
<p><strong> [root@xuegod63 httpd-2.2.25]# ls/usr/local/apache2.2-xuegod/conf/httpd.conf  </strong></p>
<p><strong> /usr/local/apache2.2-xuegod/conf/httpd.conf  </strong></p>
<p><strong> 存放网站的根目录：  </strong></p>
<p><strong> [root@xuegod63 httpd-2.2.25]#ls /usr/local/apache2.2-xuegod/htdocs/  </strong></p>
<p><strong> index.html  </strong></p>
<p><strong> 修改默认首页内容：  </strong></p>
<p><strong> [root@xuegod63 httpd-2.2.25]# echo apache-xuegod&gt;&gt; /usr/local/apache2.2-xuegod/htdocs/index.html  </strong></p>
<p><strong> 启动  apache  ：  </strong></p>
<p><strong> 配置  apache  可以开机启动并且可以使用  service  命令启动  apache  服务器  </strong></p>
<p><strong> [root@xuegod63 httpd-2.2.25]#cp /usr/local/apache2.2-xuegod/bin/apachectl /etc/init.d/apachectl-xuegod  </strong></p>
<hr>
<p><strong> 启动  apache  ：  </strong></p>
<p><strong> [<a href="mailto:root@xuegod63httpd-2.2.25" target="_blank" rel="noopener">root@xuegod63httpd-2.2.25</a>]# /etc/init.d/apachectl-xuegod start  </strong></p>
<p><strong> 测试：  </strong></p>
<p><strong> 测试：隐藏  apache  版本信息  </strong></p>
<p><strong> [root@xuegod63 ~]# curl -I192.168.1.63   #  看不到  apache  版本相关内容了  </strong></p>
<p><strong> HTTP/1.1 200 OK  </strong></p>
<p><strong> Date: Sat, 29 Aug 201509:43:44 GMT  </strong></p>
<p><strong> Server:  xuegod web server/8.1.2-dev (Unix) mod_ssl/8.1.2-devOpenSSL/1.0.0-fips  </strong></p>
<p><strong> Last-Modified: Sat, 29 Aug2015 09:37:36 GMT  </strong></p>
<p><strong> ETag: “6d086-3a-51e6ff35dba19”  </strong></p>
<p><strong> Accept-Ranges: bytes  </strong></p>
<p><strong> Content-Length: 58  </strong></p>
<p><strong> Content-Type: text/html  </strong></p>
<hr>
<p><strong> 再次隐藏版本号和系统类型  </strong></p>
<p><strong> 接下来再次修改：  </strong></p>
<hr>
<p><strong> [root@xuegod63 ~]#  vim/usr/local/apache2.2-xuegod/conf/httpd.conf  </strong></p>
<p><strong> 405 #Includeconf/extra/httpd-default.conf  </strong></p>
<p><strong> 为：  </strong></p>
<p><strong> Includeconf/extra/httpd-default.conf  </strong></p>
<hr>
<p><strong> 2  ）打开  httpd-default.conf  文件，修改如下两个地方  </strong></p>
<p><strong> [root@xuegod63 ~]#  vim/usr/local/apache2.2-xuegod/conf/extra/httpd-default.conf  </strong></p>
<p><strong> 改：  </strong></p>
<p><strong> 55 ServerTokens Full  </strong></p>
<p><strong> 65 ServerSignature On    signature  签名  </strong></p>
<p><strong> 为：  </strong></p>
<p><strong> ServerTokens Prod   #  不显示服务器操作系统类型  </strong></p>
<p><strong> ServerSignature Off   #  不显示  web  服务器版本号  </strong></p>
<hr>
<p><strong> 让  apache  配置文件生效的方法：  </strong></p>
<p><strong> 方法  1  ：重启服务：  restart  </strong></p>
<p><strong> 方法  2  ：不重启服务，重新加载配置文件  </strong></p>
<p><strong> reload  是  nginx  </strong></p>
<p><strong> [root@xuegod63 ~]#/etc/init.d/apachectl-xuegod graceful  优雅的  </strong></p>
<p><strong> 测试：  </strong></p>
<p><strong> [root@xuegod63 ~]# curl -I192.168.1.63  </strong></p>
<p><strong> HTTP/1.1 200 OK  </strong></p>
<p><strong> Date: Thu, 14 Jan 201602:31:24 GMT  </strong></p>
<p><strong> Server:  xuegod web server  </strong></p>
<hr>
<p><strong> 总结：  </strong></p>
<p><strong> 1，  安装之前改  include/ap_release.h  </strong></p>
<p><strong> 2，  Httpd.conf    extra/httpd-default.conf  去掉注释  </strong></p>
<p><strong> 3，  修改  extra/httpd-default.conf  </strong></p>
<p><strong> 55 ServerTokens Full  </strong></p>
<p><strong> 65 ServerSignature On    signature  签名  </strong></p>
<p><strong> 为：  </strong></p>
<p><strong> ServerTokens Prod   #  不显示服务器操作系统类型  </strong></p>
<p><strong> ServerSignature Off   #  不显示  web  服务器版本号  </strong></p>
<hr>
<p><strong> 2  、错误页面优雅显示  </strong></p>
<p><strong> 为了提升网站的用户体验，避免  404  ，  403  之类的丑陋的默认错误提示出现。  </strong></p>
<p><strong> <a href="http://192.18.1.63/a.html" target="_blank" rel="noopener"> http://192.18.1.63/a.html </a> </strong></p>
<p><strong> 方法一：  </strong></p>
<p><strong> [root@xuegod63 ~]# vim/usr/local/apache2.2-xuegod/conf/httpd.conf  #  在根目录的标签中添加以下红色标记内容。  </strong></p>
<p><strong> 132 <directory" usr="" local="" apache2.2-xuegod="" htdocs"=""> </directory"></strong></p>
<p><strong> 。。。  </strong></p>
<p><strong> 在大约  159  行，插入：  </strong></p>
<p><strong> ErrorDocument 404 /404.html  </strong></p>
<p><strong> 160  </strong></p>
<p><strong> 注：  #  将  404  错误跳转到  /usr/local/apache2.2-xuegod/htdocs  下的  404.html  页面上  </strong></p>
<p><strong> 注：  ErrorDocument  的命令格式如下：  </strong></p>
<p><strong> ErrorDocument  错误代码 跳转到的页面链接或文件  </strong></p>
<hr>
<p><strong> 创建  404  测试页：  </strong></p>
<p><strong> [root@xuegod63 ~]# echo”404 go to home” &gt; /usr/local/apache2.2-xuegod/htdocs/404.html  </strong></p>
<hr>
<p><strong> 重启：  </strong></p>
<p><strong> [root@xuegod63 ~]# /etc/init.d/apachectl-xuegodrestart  </strong></p>
<hr>
<hr>
<p><strong> 方法  2  ： 出错后，跳转到一个链接  </strong></p>
<p><strong> [root@xuegod63 ~]# vim/usr/local/apache2.2-xuegod/conf/httpd.conf  #  在根目录的标签中添加以下红色标记内容。  </strong></p>
<p><strong> 132 <directory" usr="" local="" apache2.2-xuegod="" htdocs"=""> </directory"></strong></p>
<p><strong> 。。。  </strong></p>
<p><strong> 在大约  159  行，插入：  </strong></p>
<p><strong> ErrorDocument 404 <a href="http://www.baidu.com" target="_blank" rel="noopener"> http://www.baidu.com </a> </strong></p>
<p><strong> 160  </strong></p>
<p><strong> 注：  #  将  404  错误跳转到  <a href="http://www.baidu.com" target="_blank" rel="noopener"> http://www.baidu.com </a> </strong></p>
<hr>
<p><strong> 重启：  </strong></p>
<p><strong> [root@xuegod63 ~]#/etc/init.d/apachectl-xuegod restart  </strong></p>
<hr>
<hr>
<p><strong> 总结：  ErrorDocument  的命令格式如下：  </strong></p>
<p><strong> ErrorDocument  错误代码 跳转到的页面或文件  </strong></p>
<p><strong> 另外这里需要注意，你若设置跳转到文件，必须要有这个文件才行。另外文件必须在站点目录内，不然会报错。  </strong></p>
<hr>
<p><strong> 终极总结：  </strong></p>
<p><strong> 1、  隐藏  web  服务的版本信息  </strong></p>
<p><strong> 2、  出现  404  错误，给它一个友好的提示  </strong></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/apache调优隐藏版本信息及404重定向/#comments" id="sourceId::2018/04/20/apache调优隐藏版本信息及404重定向/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <article id="post-Activiti工作流引擎使用" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
            <header class="article-header">
                
    
        <h1 itemprop="name">
            <a class="article-title" href="/2018/04/20/Activiti工作流引擎使用/">Activiti工作流引擎使用</a>
        </h1>
    

                
                    <div class="article-meta">
                        
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2018/04/20/Activiti工作流引擎使用/">
            <time datetime="2018-04-19T16:34:21.414Z" itemprop="datePublished">2018-04-20</time>
        </a>
    </div>


                        
                        
                    </div>
                
            </header>
        
        
        <div class="article-entry" itemprop="articleBody">
        
            
            <h1 id="Activiti工作流引擎使用"><a href="#Activiti工作流引擎使用" class="headerlink" title="Activiti工作流引擎使用"></a>Activiti工作流引擎使用</h1><h1 id="1-简单介工作流引擎与Activiti"><a href="#1-简单介工作流引擎与Activiti" class="headerlink" title="1.简单介工作流引擎与Activiti"></a>1.简单介工作流引擎与Activiti</h1><p>对于工作流引擎的解释请参考百度百科： <a href="http://baike.baidu.com/view/1636259.htm" target="_blank" rel="noopener"> 工作流引擎 </a></p>
<h3 id="1-1-我与工作流引擎"><a href="#1-1-我与工作流引擎" class="headerlink" title="1.1 我与工作流引擎"></a>1.1 我与工作流引擎</h3><p>在第一家公司工作的时候主要任务就是开发OA系统，当然基本都是有工作流的支持，不过当时使用的工作流引擎是公司一些牛人开发的（据说是用一个开源的引擎修改的），名<br>称叫CoreFlow；功能相对Activiti来说比较弱，但是能满足日常的使用，当然也有不少的问题所以后来我们只能修改引擎的代码打补丁。</p>
<p>现在是我工作的第二家公司，因为要开发ERP、OA等系统需要使用工作流，在项目调研阶段我先搜索资料选择使用哪个 <strong> 开源 </strong><br>工作流引擎，最终确定了Activiti5并基于公司的架构做了一些DEMO。</p>
<h3 id="1-2-Activiti与JBPM5？"><a href="#1-2-Activiti与JBPM5？" class="headerlink" title="1.2 Activiti与JBPM5？"></a>1.2 Activiti与JBPM5？</h3><p>对于Activiti、jBPM4、jBPM5我们应该如何选择，在InfoQ上有一篇文章写的很好，从大的层面比较各个引擎之间的差异，请参考文章： <a href="http://www.infoq.com/cn/articles/rh-
jbpm5-activiti5" target="_blank" rel="noopener"><br>纵观jBPM：从jBPM3到jBPM5以及Activiti5 </a></p>
<h3 id="1-3-Activiti资料"><a href="#1-3-Activiti资料" class="headerlink" title="1.3 Activiti资料"></a>1.3 Activiti资料</h3><ul>
<li><p>官网： <a href="http://www.activiti.org/" target="_blank" rel="noopener"> http://www.activiti.org/ </a></p>
</li>
<li><p>下载： <a href="http://www.activiti.org/download.html" target="_blank" rel="noopener"> http://www.activiti.org/download.html </a></p>
</li>
<li><p>版本：Activiti的版本是从 <strong> 5 </strong> 开始的，因为Activiti是使用jBPM4的源码； <strong> 版本发布 </strong> ：两个月发布一次。 </p>
</li>
<li><p>Eclipse Plugin: <a href="http://activiti.org/designer/update/" target="_blank" rel="noopener"> http://activiti.org/designer/update/ </a></p>
</li>
<li><p>Activit中文群：5435716 </p>
</li>
</ul>
<h2 id="2-初次使用遇到问题收集"><a href="#2-初次使用遇到问题收集" class="headerlink" title="2.初次使用遇到问题收集"></a>2.初次使用遇到问题收集</h2><p>因为Activiti刚刚退出不久所以资料比较 <strong> 空缺 </strong> ，中文资料更是少的 <strong> 可怜 </strong> ，所以开始的时候一头雾水（虽然之前用过工作流，但是感觉<br>差距很多），而且官方的手册还不是很全面；所以我把我在学习使用的过程遇到的一些疑问都罗列出来分享给大家；以下几点是我遇到和想到的，如果你还有什么疑问可以在评论<br>中和我交流再补充。</p>
<h3 id="2-1-部署流程图后中文乱码"><a href="#2-1-部署流程图后中文乱码" class="headerlink" title="2.1 部署流程图后中文乱码"></a>2.1 部署流程图后中文乱码</h3><p><strong> 乱码 </strong> 是一直缠绕着国人的问题，之前各个技术、工具出现乱码的问题写过很多文章，这里也不例外……，Activiti的乱码问题在流程图中。 </p>
<p>流程图的乱码如下图所示：</p>
<p><img src="http://static.open-
open.com/lib/uploadImg/20121017/20121017155144_803.png" alt="Activiti工作流引擎使用"></p>
<p>解决办法有两种：</p>
<h4 id="2-1-1-修改源代码方式"><a href="#2-1-1-修改源代码方式" class="headerlink" title="2.1.1 修改源代码方式"></a>2.1.1 修改源代码方式</h4><p>修改源码</p>
<pre><code>org.activiti.engine.impl.bpmn.diagram.ProcessDiagramCanvas
</code></pre><p>在构造方法</p>
<pre><code>public ProcessDiagramCanvas(int width, int height)
</code></pre><p>中有一行代码是设置字体的，默认是用  <strong> Arial </strong> 字体，这就是乱码产生的原因，把字改为本地的中文字体即可，例如：</p>
<pre><code>Font font = new Font(&quot;WenQuanYi Micro Hei&quot;, Font.BOLD, 11);
</code></pre><p>当然如果你有配置文件读取工具那么可以设置在*.properties文件中，我就是这么做的：</p>
<pre><code>Font font = new Font(PropertyFileUtil.get(&quot;activiti.diagram.canvas.font&quot;), Font.BOLD, 11);
</code></pre><h4 id="2-1-2-使用压缩包方式部署"><a href="#2-1-2-使用压缩包方式部署" class="headerlink" title="2.1.2 使用压缩包方式部署"></a>2.1.2 使用压缩包方式部署</h4><p>Activiti支持部署*.bpmn20.xml、bar、zip格式的流程定义。</p>
<p>使用Activit Deisigner工具设计流程图的时候会有三个类型的文件:</p>
<ul>
<li><p>.activiti设计工具使用的文件 </p>
</li>
<li><p>.bpmn20.xml设计工具自动根据.activiti文件生成的xml文件 </p>
</li>
<li><p>.png流程图图片 </p>
</li>
</ul>
<p>解决办法就是把xml文件和图片文件同时部署，因为在单独部署xml文件的时候Activiti会自动生成一张流程图的图片文件，但是这样在使用的时候坐标和图片对应<br>不起来……</p>
<p>所以把xml和图片同时部署的时候Activiti自动关联xml和图片，当需要获取图片的时候直接返回部署时压缩包里面的图片文件，而不是Activiti自动生成<br>的图片文件</p>
<h5 id="2-1-2-1-使用工具打包Bar文件"><a href="#2-1-2-1-使用工具打包Bar文件" class="headerlink" title="2.1.2.1 使用工具打包Bar文件"></a>2.1.2.1 使用工具打包Bar文件</h5><p>右键项目名称然后点击“Create deployment artifacts”，会在src目录中创建 <strong> deployment </strong><br>文件夹，里面包含*.bar文件.</p>
<h5 id="2-1-2-2-使用Ant脚本打包Zip文件"><a href="#2-1-2-2-使用Ant脚本打包Zip文件" class="headerlink" title="2.1.2.2 使用Ant脚本打包Zip文件"></a>2.1.2.2 使用Ant脚本打包Zip文件</h5><p>这也是我们采用的办法，你可以手动选择xml和png打包成zip格式的文件，也可以像我们一样采用ant target的方式打包这两个文件。</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project name=&quot;foo&quot;&gt;

    &lt;property name=&quot;workflow.definition&quot; value=&quot;foo-common-core/src/main/resources/diagrams&quot; /&gt;
    &lt;property name=&quot;workflow.deployments&quot; value=&quot;foo-common-core/src/main/resources/deployments&quot; /&gt;

&lt;target name=&quot;workflow.package.oa.leave&quot;&gt;
        &lt;echo&gt;打包流程定义及流程图::OA-请假&lt;/echo&gt;
        &lt;zip destfile=&quot;${workflow.deployments}/oa/leave.zip&quot; basedir=&quot;${workflow.definition}/oa/leave&quot; update=&quot;true&quot;
            includes=&quot;*.xml,*.png&quot; /&gt;
    &lt;/target&gt;
&lt;/project&gt;
</code></pre><p>这样当修改流程定义文件后只要运行ant命令就可以打包了：</p>
<pre><code>ant workflow.package.oa.leave
</code></pre><p>现在部署bar或者zip文件查看流程图图片就不是乱码了，而是你的压缩包里面的png文件。</p>
<h3 id="2-2-使用引擎提供的Form还是自定义业务Form"><a href="#2-2-使用引擎提供的Form还是自定义业务Form" class="headerlink" title="2.2 使用引擎提供的Form还是自定义业务Form"></a>2.2 使用引擎提供的Form还是自定义业务Form</h3><h4 id="2-2-1-引擎提供的Form"><a href="#2-2-1-引擎提供的Form" class="headerlink" title="2.2.1 引擎提供的Form"></a>2.2.1 引擎提供的Form</h4><p>定义表单的方式在每个Task标签中定义 <strong> extensionElements </strong> 和 <strong> activiti:formProperty </strong><br>即可，到达这个节点的时候可以通过API读取表单元素。</p>
<p>Activiti官方的例子使用的就是在流程定义中设置每一个节点显示什么样的表单哪些字段需要显示、哪些字段只读、哪些字段必填。</p>
<p>但是这种方式仅仅适用于比较简单的流程，对于稍微复杂或者页面需要业务逻辑的判断的情况就不适用了。</p>
<p>对于数据的保存都是在引擎的表中，不利于和其他表的关联、对整个系统的规划也不利！</p>
<h4 id="2-2-2-自定义业务Form"><a href="#2-2-2-自定义业务Form" class="headerlink" title="2.2.2 自定义业务Form"></a>2.2.2 自定义业务Form</h4><p>这种方式应该是大家用的最多的了，因为一般的业务系统业务逻辑都会比较复杂，而且数据库中很多表都会有依赖关系，表单中有很多状态判断。</p>
<p>例如我们的系统适用jQuery UI作为UI，有很多javascript代码，页面的很多操作需要特殊处理（例如：多个选项的互斥、每个节点根据类型和操作人显示<br>不同的按钮）；基本每个公司都有一套自己的UI风格，要保持多个系统的操作习惯一致只能使用自定义表单才能满足。</p>
<h3 id="2-3-业务和流程的关联方式"><a href="#2-3-业务和流程的关联方式" class="headerlink" title="2.3 业务和流程的关联方式"></a>2.3 业务和流程的关联方式</h3><p>这个问题在群里面很多人都问过，这也是我刚刚开始迷惑的地方；</p>
<p>后来看了以下API发现RuntimeService有两个方法：</p>
<h3 id="2-3-1-startProcessInstanceByKey"><a href="#2-3-1-startProcessInstanceByKey" class="headerlink" title="2.3.1 startProcessInstanceByKey"></a>2.3.1 startProcessInstanceByKey</h3><p>javadoc对其说明：</p>
<pre><code>startProcessInstanceByKey(String processDefinitionKey, Map variabes) 
          Starts a new process instance in the latest version of the process definition with the given key
</code></pre><p>其中 <strong> businessKey </strong><br>就是业务ID，例如要申请请假，那么先填写登记信息，然后（保存+启动流程），因为请假是单独设计的数据表，所以保存后得到实体ID就可以把它传给 <strong><br>processInstanceBusinessKey </strong> 方法启动流程。当需要根据businessKey查询流程的时候就可以通过API查询:</p>
<pre><code>runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(processInstanceBusinessKey, processDefinitionKey);
</code></pre><p>建  <strong> 议数据库冗余设计 </strong> ：在业务表设计的时候添加一列：  <strong> PROCESS_INSTANCE_ID varchar2(64) </strong><br>，在流程启动之后把流程ID更新到业务表中，这样不管从业务还是流程都可以查询到对方！</p>
<p><strong> 特别说明： </strong> 此方法启动时自动选择最新版本的流程定义。 </p>
<h3 id="2-3-2-startProcessInstanceById"><a href="#2-3-2-startProcessInstanceById" class="headerlink" title="2.3.2 startProcessInstanceById"></a>2.3.2 startProcessInstanceById</h3><p>javadoc对其说明：</p>
<pre><code>startProcessInstanceById(String processDefinitionId, String businessKey, Map variables) 
          Starts a new process instance in the exactly specified version of the process definition with the given id.
</code></pre><p><strong> processDefinitionId </strong> ：这个参数的值可以通过 <strong> repositoryService.createProcessDefinitionQuery() </strong> 方法查询，对应数据库： <strong> ACT_RE_PROCDEF </strong> ；每次部署一次流程定义就会添加一条数据，同名的版本号累加。 </p>
<p><strong> 特别说明： </strong> 此可以指定不同版本的流程定义，让用户多一层选择。 </p>
<h3 id="2-3-3-如何选择"><a href="#2-3-3-如何选择" class="headerlink" title="2.3.3 如何选择"></a>2.3.3 如何选择</h3><p>建议使用 <strong> startProcessInstanceByKey </strong> ，特殊情况需要使用以往的版本选择使用 <strong><br>startProcessInstanceById </strong> 。</p>
<h3 id="2-4-同步用户数据"><a href="#2-4-同步用户数据" class="headerlink" title="2.4 同步用户数据"></a>2.4 同步用户数据</h3><p>这个问题也是比较多的人询问过，Activiti支持对任务分配到：指定人、指定组、两者组合，而这些人和组的信息都保存在 <strong> ACT_ID.. </strong> 表中，有<br>自己的用户和组(角色)管理让很多人不知所措了；原因是因为每个系统都会存在一个权限管理模块（维护：用户、部门、角色、授权），不知道该怎么和Activiti同步<br>。</p>
<h4 id="2-4-1-建议处理方式"><a href="#2-4-1-建议处理方式" class="headerlink" title="2.4.1 建议处理方式"></a>2.4.1 建议处理方式</h4><p>Activiti有一个 <strong> IdentityService </strong> 接口，通过这个接口可以操控Activiti的ACT_ID_*表的数据，一般的做法是用业务<br>系统的权限管理模块维护用户数据，当进行CRUD操作的时候在原有业务逻辑后面添加同步到Activiti的代码；例如添加一个用户时同步Activiti<br>User的代码片段：</p>
<pre><code>/**
 * 保存用户信息 并且同步用户信息到activiti的identity.User，同时设置角色
 * @param user
 * @param roleIds
 */
public void saveUser(User user, List&lt;Long&gt; roleIds, boolean synToActiviti) {
    accountManager.saveEntity(user);
    String userId = user.getId().toString();

    if (synToActiviti) {
        List&lt;org.activiti.engine.identity.User&gt; activitiUsers = identityService.createUserQuery().userId(userId).list();
        if (activitiUsers.size() == 1) {
            //更新信息
            org.activiti.engine.identity.User activitiUser = activitiUsers.get(0);
            activitiUser.setFirstName(user.getName());
            activitiUser.setLastName(&quot;&quot;);
            activitiUser.setPassword(user.getPassword());
            activitiUser.setEmail(user.getEmail());
            identityService.saveUser(activitiUser);

            // 删除用户的membership
            List&lt;Group&gt; activitiGroups = identityService.createGroupQuery().groupMember(userId).list();
            for (Group group : activitiGroups) {
                identityService.deleteMembership(userId, group.getId());
            }

            // 添加membership
            for (Long roleId : roleIds) {
                Role role = roleManager.getEntity(roleId);
                identityService.createMembership(userId, role.getEnName());
            }

        } else {
            org.activiti.engine.identity.User newUser = identityService.newUser(userId);
            newUser.setFirstName(user.getName());
            newUser.setLastName(&quot;&quot;);
            newUser.setPassword(user.getPassword());
            newUser.setEmail(user.getEmail());
            identityService.saveUser(newUser);

            // 添加membership
            for (Long roleId : roleIds) {
                Role role = roleManager.getEntity(roleId);
                identityService.createMembership(userId, role.getEnName());
            }
        }
    }

}
</code></pre><p>删除操作也和这个类似！</p>
<p>不管从业务系统维护用户还是从Activiti维护，肯定要确定一方，然后CRUD的时候同步到对方，如果需要同步多个子系统那么可以再调用WebService实现<br>。</p>
<h3 id="2-5-流程图设计工具用什么"><a href="#2-5-流程图设计工具用什么" class="headerlink" title="2.5 流程图设计工具用什么"></a>2.5 流程图设计工具用什么</h3><p>Activiti提供了两个流程设计工具，但是面向对象不同。</p>
<ul>
<li><p>Activiti Modeler，面向业务人员，使用开源的BPMN设计工具 <a href="http://www.signavio.com/en.html" target="_blank" rel="noopener"> Signavio </a> ，使用BPMN描述业务流程图 </p>
</li>
<li><p>Eclipse Designer，面向开发人员，Eclipse的插件，可以让开发人员定制每个节点的属性（ID、Name、Listener、Attr等） </p>
</li>
</ul>
<h4 id="2-5-1-我们的方式"><a href="#2-5-1-我们的方式" class="headerlink" title="2.5.1 我们的方式"></a>2.5.1 我们的方式</h4><p>可能你会惊讶，因为我们没有使用Activiti Modeler，我们认为用Viso已经能表达流程图的意思了，而且项目经理也是技术出身，和开发人员也容易沟通。</p>
<p>目前这个项目是第一个使用Activiti的，开始我们在需求调研阶段使用Viso设计流程图，利用 <a href="http://wiki.mbalib.com/wiki/%E6%B3%B3%E9%81%93%E6%B5%81%E7%A8%8B%E5%9B%BE" target="_blank" rel="noopener"> 泳道流程图
</a><br>设计和客户沟通，确定后由负责流程的开发人员用Eclipse Designer设计得到bpmn20.xml，最后部署。</p>
<h3 id="2-6-Eclipse-Designer存在的问题"><a href="#2-6-Eclipse-Designer存在的问题" class="headerlink" title="2.6 Eclipse Designer存在的问题"></a>2.6 Eclipse Designer存在的问题</h3><p>这个插件有一个很讨厌的Bug一直未修复，安装了插件后Eclipse的复制和粘帖快捷键会被更换为(Ctrl+Insert、Shift+Insert)；Bug描<br>述请见：</p>
<ul>
<li><p><a href="http://forums.activiti.org/en/viewtopic.php?f=8&amp;t=2701" target="_blank" rel="noopener"> Activit Forums中报告的Bug </a></p>
</li>
<li><p><a href="http://jira.codehaus.org/browse/ACT-992" target="_blank" rel="noopener"> Jira的登记 </a></p>
</li>
</ul>
<p>所以最后我们只能单独开一个安装了Eclipse Designer的Eclipse专门用来设计流程图，这样就不影响正常使用Eclipse JAVAEE了。</p>
<h2 id="3-配置"><a href="#3-配置" class="headerlink" title="3.配置"></a>3.配置</h2><h3 id="3-1-集成Spring"><a href="#3-1-集成Spring" class="headerlink" title="3.1 集成Spring"></a>3.1 集成Spring</h3><p>对于和Spring的集成Activiti做的不错，简单配置一些Bean代理即可实现，但是有两个和事务相关的地方要提示：</p>
<ul>
<li><p>配置 <strong> processEngineConfiguration </strong> 的时候属性 <strong> transactionManager </strong> 要使用和业务功能的同一个事务管理Bean，否则事务不同步。 </p>
</li>
<li><p>对于实现了org.activiti.engine.delegate包中的接口的类需要被事务控制的实现类需要被Spring代理，并且添加事务的Annotation或者在xml中配置，例如: </p>
</li>
</ul>
<pre><code>/**
 * 创建缴费流程的时候自动创建实体
 *
 * @author HenryYan
 */
@Service
@Transactional
publicclass CreatePaymentProcessListener implementsExecutionListener {
   ....
}
</code></pre><h2 id="4-使用单元测试"><a href="#4-使用单元测试" class="headerlink" title="4.使用单元测试"></a>4.使用单元测试</h2><p>单元测试均使用Spring的AbstractTransactionalJUnit4SpringContextTests作为SuperClass，并且在测试类<br>添加：</p>
<pre><code>@ContextConfiguration(locations = { &quot;/applicationContext-test.xml&quot;})
@RunWith(SpringJUnit4ClassRunner.class)
</code></pre><p>虽然Activiti也提供了测试的一些超类，但是感觉不好用，所以自己封装了一些方法。</p>
<p>代码请转移： <a href="https://gist.github.com/2182847" target="_blank" rel="noopener"> https://gist.github.com/2182847 </a></p>
<h3 id="4-1-验证流程图设计是否正确"><a href="#4-1-验证流程图设计是否正确" class="headerlink" title="4.1 验证流程图设计是否正确"></a>4.1 验证流程图设计是否正确</h3><p>代码请转移： <a href="https://gist.github.com/2182869" target="_blank" rel="noopener"> https://gist.github.com/2182869 </a></p>
<h3 id="4-2-业务对象和流程关联测试"><a href="#4-2-业务对象和流程关联测试" class="headerlink" title="4.2 业务对象和流程关联测试"></a>4.2 业务对象和流程关联测试</h3><p>代码请转移： <a href="https://gist.github.com/2182973" target="_blank" rel="noopener"> https://gist.github.com/2182973 </a></p>
<h2 id="5-各种状态的任务查询以及和业务对象关联"><a href="#5-各种状态的任务查询以及和业务对象关联" class="headerlink" title="5.各种状态的任务查询以及和业务对象关联"></a>5.各种状态的任务查询以及和业务对象关联</h2><p>我们目前分为4中状态：未签收、办理中、运行中、已完成。</p>
<p>查询到任务或者流程实例后要显示在页面，这个时候需要添加业务数据，最终结果就是业务和流程的并集，请参考 <strong> 6.2 </strong> 。</p>
<h3 id="5-1-未签收-Task"><a href="#5-1-未签收-Task" class="headerlink" title="5.1 未签收(Task)"></a>5.1 未签收(Task)</h3><p>此类任务针对于把Task分配给一个角色时，例如部门领导，因为 <strong> 部门领导 </strong> 角色可以指定多个人所以需要先签收再办理，术语： <strong> 抢占式 </strong></p>
<p>对应的API查询：</p>
<pre><code>/**
 * 获取未签收的任务查询对象
 * @param userId    用户ID
 */
@Transactional(readOnly = true)
publicTaskQuery createUnsignedTaskQuery(String userId) {
    TaskQuery taskCandidateUserQuery = taskService.createTaskQuery().processDefinitionKey(getProcessDefKey())
            .taskCandidateUser(userId);
    returntaskCandidateUserQuery;
}
</code></pre><h3 id="5-2-办理中-Task"><a href="#5-2-办理中-Task" class="headerlink" title="5.2 办理中(Task)"></a>5.2 办理中(Task)</h3><p>此类任务数据类源有两种:</p>
<ul>
<li><p>签收后的，5.1中签收后就应该为办理中状态 </p>
</li>
<li><p>节点指定的是具体到一个人，而不是角色 </p>
</li>
</ul>
<p>对应的API查询：</p>
<pre><code>/**
 * 获取正在处理的任务查询对象
 * @param userId    用户ID
 */
@Transactional(readOnly = true)
publicTaskQuery createTodoTaskQuery(String userId) {
    TaskQuery taskAssigneeQuery = taskService.createTaskQuery().processDefinitionKey(getProcessDefKey()).taskAssignee(userId);
    returntaskAssigneeQuery;
}
</code></pre><h3 id="5-3-运行中-ProcessInstance"><a href="#5-3-运行中-ProcessInstance" class="headerlink" title="5.3 运行中(ProcessInstance)"></a>5.3 运行中(ProcessInstance)</h3><p>说白了就是没有结束的流程，所有参与过的人都应该可以看到这个实例，但是Activiti的API没有可以通过用户查询的方法，这个只能自己用hack的方式处理了，<br>我目前还没有处理。</p>
<p>从表 <strong> ACT_RU_EXECUTION </strong> 中查询数据。</p>
<p>对应的API查询：</p>
<pre><code>/**
 * 获取未经完成的流程实例查询对象
 * @param userId    用户ID
 */
@Transactional(readOnly = true)
publicProcessInstanceQuery createUnFinishedProcessInstanceQuery(String userId) {
    ProcessInstanceQuery unfinishedQuery = runtimeService.createProcessInstanceQuery().processDefinitionKey(getProcessDefKey())
            .active();
    returnunfinishedQuery;
}
</code></pre><h3 id="5-4-已完成-HistoricProcessInstance"><a href="#5-4-已完成-HistoricProcessInstance" class="headerlink" title="5.4 已完成(HistoricProcessInstance)"></a>5.4 已完成(HistoricProcessInstance)</h3><p>已经结束的流程实例。</p>
<p>从表 <strong> ACT_HI_PROCINST </strong> 中查询数据。</p>
<pre><code>/**
 * 获取已经完成的流程实例查询对象
 * @param userId    用户ID
 */
@Transactional(readOnly = true)
publicHistoricProcessInstanceQuery createFinishedProcessInstanceQuery(String userId) {
    HistoricProcessInstanceQuery finishedQuery = historyService.createHistoricProcessInstanceQuery()
            .processDefinitionKey(getProcessDefKey()).finished();
    returnfinishedQuery;
}
</code></pre><h3 id="5-5-查询时和业务关联"><a href="#5-5-查询时和业务关联" class="headerlink" title="5.5 查询时和业务关联"></a>5.5 查询时和业务关联</h3><p>提示：之前在业务对象添加了 <strong> PROCESS_INSTANCE_ID </strong> 字段</p>
<p>思路：现在可以利用这个字段查询了，不管是Task还是ProcessInstance都可以得到流程实例ID，可以根据流程实例ID查询实体然后把流程对象设置到实<br>体的一个属性中由Action或者Controller输出到前台。</p>
<p>代码请参考： <a href="https://gist.github.com/2183557" target="_blank" rel="noopener"> https://gist.github.com/2183557 </a></p>
<h2 id="6-UI及截图"><a href="#6-UI及截图" class="headerlink" title="6.UI及截图"></a>6.UI及截图</h2><p>结合实际业务描述一个业务从开始到结束的过程，对于迷惑的同学看完豁然开朗了；这里使用请假作为例子。</p>
<h3 id="6-1-单独一个列表负责申请"><a href="#6-1-单独一个列表负责申请" class="headerlink" title="6.1 单独一个列表负责申请"></a>6.1 单独一个列表负责申请</h3><p>这样的好处是申请和流程办理分离开处理，列表显示未启动流程的请假记录（数据库PROCESS_INSTANCE_ID为空）。</p>
<p>申请界面的截图：</p>
<p><img src="http://static.open-
open.com/lib/uploadImg/20121017/20121017155145_849.png" alt="Activiti工作流引擎使用"></p>
<h3 id="6-2-流程状态"><a href="#6-2-流程状态" class="headerlink" title="6.2 流程状态"></a>6.2 流程状态</h3><p><img src="http://static.open-
open.com/lib/uploadImg/20121017/20121017155146_668.png" alt="Activiti工作流引擎使用"></p>
<h3 id="6-3-流程跟踪"><a href="#6-3-流程跟踪" class="headerlink" title="6.3 流程跟踪"></a>6.3 流程跟踪</h3><p>图片方式显示当前节点：</p>
<p><img src="http://static.open-
open.com/lib/uploadImg/20121017/20121017155147_20.png" alt="Activiti工作流引擎使用"></p>
<p>列表形式显示流程流转过程:</p>
<p><img src="http://static.open-
open.com/lib/uploadImg/20121017/20121017155147_321.png" alt="Activiti工作流引擎使用"></p>
<h4 id="6-3-1-当前节点定位JS"><a href="#6-3-1-当前节点定位JS" class="headerlink" title="6.3.1 当前节点定位JS"></a>6.3.1 当前节点定位JS</h4><p><strong> Java </strong> 代码请移步： <a href="https://gist.github.com/2183712" target="_blank" rel="noopener"> https://gist.github.com/2183712 </a></p>
<p><strong> Javascript </strong> 思路：先通过Ajax获取当前节点的坐标，在指定位置添加红色边框，然后加载图片。 </p>
<p>代码移步： <a href="https://gist.github.com/2183804" target="_blank" rel="noopener"> https://gist.github.com/2183804 </a></p>
<h2 id="7-开启Logger"><a href="#7-开启Logger" class="headerlink" title="7.开启Logger"></a>7.开启Logger</h2><ol>
<li>添加log4j的jar </li>
<li>设置 <strong> log4j.logger.java.sql </strong> =DEBUG </li>
</ol>
<h2 id="8-结束"><a href="#8-结束" class="headerlink" title="8.结束"></a>8.结束</h2><p>之前就想写这篇文章，现在终于完成了，花费了几个小时，希望能节省你几天的时间。</p>
<p>请读者仔细阅读Activiti的用户手册和Javadoc。  </p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">


    <div class="bdsharebuttonbox">
    <a href="#" class="bds_more" data-cmd="more">分享到：</a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间">QQ空间</a>
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博">新浪微博</a>
    <a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博">腾讯微博</a>
    <a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网">人人网</a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信">微信</a>
</div>
<script>
window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{"bdSize":16}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
</script>
<style>
    .bdshare_popup_box {
        border-radius: 4px;
        border: #e1e1e1 solid 1px;
    }
    .bdshare-button-style0-16 a,
    .bdshare-button-style0-16 .bds_more {
        padding-left: 20px;
        margin: 6px 10px 6px 0;
    }
    .bdshare_dialog_list a,
    .bdshare_popup_list a,
    .bdshare_popup_bottom a {
        font-family: 'Microsoft Yahei';
    }
    .bdshare_popup_top {
        display: none;
    }
    .bdshare_popup_bottom {
        height: auto;
        padding: 5px;
    }
</style>


</div>

            
    
        <a href="https://www.itchina.top/2018/04/20/Activiti工作流引擎使用/#comments" id="sourceId::2018/04/20/Activiti工作流引擎使用/" class="article-comment-link cy_cmt_count">评论</a>
    

        </footer>
    </div>
    
</article>



    <nav id="page-nav">
        <a class="extend prev" rel="prev" href="/page/4/">&laquo; 上一页</a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/3/">3</a><a class="page-number" href="/page/4/">4</a><span class="page-number current">5</span><a class="page-number" href="/page/6/">6</a><a class="page-number" href="/page/7/">7</a><a class="extend next" rel="next" href="/page/6/">下一页 &raquo;</a>
    </nav>
</section>
            
                
<aside id="sidebar">
   
        
    <div class="widget-wrap">
        <h3 class="widget-title">最新文章</h3>
        <div class="widget">
            <ul id="recent-post" class="no-thumbnail">
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/IDE/">IDE</a></p>
                            <p class="item-title"><a href="/2018/04/20/项目相关的CVS操作/" class="title">项目相关的CVS操作</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.467Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/J2EE/">J2EE</a></p>
                            <p class="item-title"><a href="/2018/04/20/图片转换PDF文件/" class="title">图片转换PDF文件</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.449Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"><a class="article-category-link" href="/categories/Spring/">Spring</a></p>
                            <p class="item-title"><a href="/2018/04/20/使用 Spring Data JPA 简化 JPA 开发/" class="title">使用 Spring Data JPA 简化 JPA 开发</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.448Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2018/04/20/通过Ajax进行POST提交JSON类型的数据到SpringMVC Controller的方法/" class="title">通过Ajax进行POST提交JSON类型的数据到SpringMVC Controller的方法</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.448Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
                    <li>
                        
                        <div class="item-inner">
                            <p class="item-category"></p>
                            <p class="item-title"><a href="/2018/04/20/面试感悟----一名3年工作经验的程序员应该具备的技能/" class="title">面试感悟----一名3年工作经验的程序员应该具备的技能</a></p>
                            <p class="item-date"><time datetime="2018-04-19T16:34:21.447Z" itemprop="datePublished">2018-04-20</time></p>
                        </div>
                    </li>
                
            </ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">分类</h3>
        <div class="widget">
            <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/ActiveMQ/">ActiveMQ</a><span class="category-list-count">7</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/ExtJs/">ExtJs</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Git/">Git</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/IDE/">IDE</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/J2EE/">J2EE</a><span class="category-list-count">7</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/J2EE/Spring/">Spring</a><span class="category-list-count">1</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/Linux/">Linux</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Redis/">Redis</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/Spring/">Spring</a><span class="category-list-count">21</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/cnn图片数据处理、显示/">cnn图片数据处理、显示</a><span class="category-list-count">1</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/cnn图片数据处理、显示/python数据分析/">python数据分析</a><span class="category-list-count">1</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/web前端/">web前端</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/数据库/">数据库</a><span class="category-list-count">3</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/数据库/集群/">集群</a><span class="category-list-count">1</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/集群/">集群</a><span class="category-list-count">2</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">归档</h3>
        <div class="widget">
            <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/04/">四月 2018</a><span class="archive-list-count">65</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">标签</h3>
        <div class="widget">
            <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Apache/">Apache</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Docker/">Docker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Druid/">Druid</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ImageToPDF/">ImageToPDF</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Logstash/">Logstash</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Spring-Boot/">Spring Boot</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/activemq/">activemq</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ajax/">ajax</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/angular/">angular</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cas/">cas</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/">docker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/exception/">exception</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ext/">ext</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/extjs/">extjs</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/find/">find</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/github/">github</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ide/">ide</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/idea/">idea</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/java/">java</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/javascript/">javascript</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jboss/">jboss</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jmx/">jmx</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jpa/">jpa</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linux/">linux</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/lombok/">lombok</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mvc/">mvc</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mybatis/">mybatis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mysql/">mysql</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/">nginx</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/numpy/">numpy</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/seo/">seo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/server/">server</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/shiro/">shiro</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring/">spring</a><span class="tag-list-count">17</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring-mvc/">spring mvc</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring-boo/">spring-boo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sql-server/">sql server</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/windows/">windows</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zookeeper/">zookeeper</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/优化/">优化</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/分布式日志/">分布式日志</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/多线程/">多线程</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/工作/">工作</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/异常/">异常</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/异常处理/">异常处理</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/搜索引擎/">搜索引擎</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据分析/">数据分析</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/汉字转拼音/">汉字转拼音</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/版本控制系统/">版本控制系统</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/程序员/">程序员</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/经验/">经验</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/集群/">集群</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/面试/">面试</a><span class="tag-list-count">1</span></li></ul>
        </div>
    </div>

    
        
    <div class="widget-wrap">
        <h3 class="widget-title">标签云</h3>
        <div class="widget tagcloud">
            <a href="/tags/Apache/" style="font-size: 10px;">Apache</a> <a href="/tags/Docker/" style="font-size: 10px;">Docker</a> <a href="/tags/Druid/" style="font-size: 10px;">Druid</a> <a href="/tags/ImageToPDF/" style="font-size: 10px;">ImageToPDF</a> <a href="/tags/Logstash/" style="font-size: 10px;">Logstash</a> <a href="/tags/Spring-Boot/" style="font-size: 10px;">Spring Boot</a> <a href="/tags/activemq/" style="font-size: 18px;">activemq</a> <a href="/tags/ajax/" style="font-size: 10px;">ajax</a> <a href="/tags/angular/" style="font-size: 10px;">angular</a> <a href="/tags/cas/" style="font-size: 10px;">cas</a> <a href="/tags/docker/" style="font-size: 10px;">docker</a> <a href="/tags/exception/" style="font-size: 10px;">exception</a> <a href="/tags/ext/" style="font-size: 14px;">ext</a> <a href="/tags/extjs/" style="font-size: 14px;">extjs</a> <a href="/tags/find/" style="font-size: 10px;">find</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/github/" style="font-size: 10px;">github</a> <a href="/tags/ide/" style="font-size: 12px;">ide</a> <a href="/tags/idea/" style="font-size: 10px;">idea</a> <a href="/tags/java/" style="font-size: 16px;">java</a> <a href="/tags/javascript/" style="font-size: 12px;">javascript</a> <a href="/tags/jboss/" style="font-size: 10px;">jboss</a> <a href="/tags/jmx/" style="font-size: 10px;">jmx</a> <a href="/tags/jpa/" style="font-size: 10px;">jpa</a> <a href="/tags/linux/" style="font-size: 14px;">linux</a> <a href="/tags/lombok/" style="font-size: 10px;">lombok</a> <a href="/tags/mvc/" style="font-size: 10px;">mvc</a> <a href="/tags/mybatis/" style="font-size: 10px;">mybatis</a> <a href="/tags/mysql/" style="font-size: 12px;">mysql</a> <a href="/tags/nginx/" style="font-size: 10px;">nginx</a> <a href="/tags/numpy/" style="font-size: 10px;">numpy</a> <a href="/tags/seo/" style="font-size: 10px;">seo</a> <a href="/tags/server/" style="font-size: 10px;">server</a> <a href="/tags/shiro/" style="font-size: 12px;">shiro</a> <a href="/tags/spring/" style="font-size: 20px;">spring</a> <a href="/tags/spring-mvc/" style="font-size: 10px;">spring mvc</a> <a href="/tags/spring-boo/" style="font-size: 10px;">spring-boo</a> <a href="/tags/sql-server/" style="font-size: 10px;">sql server</a> <a href="/tags/windows/" style="font-size: 10px;">windows</a> <a href="/tags/zookeeper/" style="font-size: 10px;">zookeeper</a> <a href="/tags/优化/" style="font-size: 12px;">优化</a> <a href="/tags/分布式日志/" style="font-size: 10px;">分布式日志</a> <a href="/tags/多线程/" style="font-size: 10px;">多线程</a> <a href="/tags/工作/" style="font-size: 10px;">工作</a> <a href="/tags/异常/" style="font-size: 10px;">异常</a> <a href="/tags/异常处理/" style="font-size: 10px;">异常处理</a> <a href="/tags/搜索引擎/" style="font-size: 12px;">搜索引擎</a> <a href="/tags/数据分析/" style="font-size: 10px;">数据分析</a> <a href="/tags/汉字转拼音/" style="font-size: 10px;">汉字转拼音</a> <a href="/tags/版本控制系统/" style="font-size: 10px;">版本控制系统</a> <a href="/tags/程序员/" style="font-size: 10px;">程序员</a> <a href="/tags/经验/" style="font-size: 10px;">经验</a> <a href="/tags/集群/" style="font-size: 10px;">集群</a> <a href="/tags/面试/" style="font-size: 10px;">面试</a>
        </div>
    </div>

    
        
    <div class="widget-wrap widget-list">
        <h3 class="widget-title">链接</h3>
        <div class="widget">
            <ul>
                
                    <li>
                        <a href="http://www.baidu.com">百度</a>
                    </li>
                
            </ul>
        </div>
    </div>


    
    <div id="toTop" class="fa fa-angle-up"></div>
</aside>

            
        </div>
        <!--google 底部广告 -->
<div style="width:100%;text-align: center;clear: both;margin-bottom: 20px;" class="hidden-xs">
    <script async src="http://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <!-- 博客footer上方的广告 -->
    <ins class="adsbygoogle"
         style="display:block"
         data-ad-client="ca-pub-8254913025324557"
         data-ad-slot="3369473724"
         data-ad-format="auto"></ins>
    <script>
        (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<footer id="footer">
    <div class="outer">
        <div id="footer-info" class="inner">
            &copy;2018 - 2018&nbsp; LErry Li &nbsp;版权所有<br>
			<a href="http://www.miitbeian.gov.cn">沪ICP备17054498号-3</a>
        </div>
    </div>
</footer>
        
    
    <script id="cy_cmt_num" src="https://changyan.sohu.com/upload/plugins/plugins.list.count.js?clientId=cytxPSIHr"></script>
    <script charset="utf-8" type="text/javascript" src="https://changyan.sohu.com/upload/changyan.js" ></script>
    <script type="text/javascript">
    window.changyan.api.config({
    appid: 'cytxPSIHr',
    conf: 'prod_3c92d05d8ba4377e48c5fe27c2159761'
    });
    </script>




    
        <script src="/libs/lightgallery/js/lightgallery.min.js"></script>
        <script src="/libs/lightgallery/js/lg-thumbnail.min.js"></script>
        <script src="/libs/lightgallery/js/lg-pager.min.js"></script>
        <script src="/libs/lightgallery/js/lg-autoplay.min.js"></script>
        <script src="/libs/lightgallery/js/lg-fullscreen.min.js"></script>
        <script src="/libs/lightgallery/js/lg-zoom.min.js"></script>
        <script src="/libs/lightgallery/js/lg-hash.min.js"></script>
        <script src="/libs/lightgallery/js/lg-share.min.js"></script>
        <script src="/libs/lightgallery/js/lg-video.min.js"></script>
    
    
        <script src="/libs/justified-gallery/jquery.justifiedGallery.min.js"></script>
    
    



<!-- Custom Scripts -->
<script src="/js/main.js"></script>

    </div>
</body>
</html>